UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

541 lines (538 loc) • 20.3 kB
/** * DevExtreme (cjs/__internal/ui/m_responsive_box.js) * Version: 24.2.6 * Build date: Mon Mar 17 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _component_registrator = _interopRequireDefault(require("../../core/component_registrator")); var _renderer = _interopRequireDefault(require("../../core/renderer")); var _common = require("../../core/utils/common"); var _extend = require("../../core/utils/extend"); var _iterator = require("../../core/utils/iterator"); var _size = require("../../core/utils/size"); var _type = require("../../core/utils/type"); var _window = require("../../core/utils/window"); var _box = _interopRequireDefault(require("../../ui/box")); var _uiCollection_widget = _interopRequireDefault(require("../../ui/collection/ui.collection_widget.edit")); var _ui = _interopRequireDefault(require("../../ui/widget/ui.errors")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e } } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function(n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) { ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]) } } return n }, _extends.apply(null, arguments) } const RESPONSIVE_BOX_CLASS = "dx-responsivebox"; const SCREEN_SIZE_CLASS_PREFIX = "dx-responsivebox-screen-"; const BOX_ITEM_CLASS = "dx-box-item"; const BOX_ITEM_DATA_KEY = "dxBoxItemData"; const HD_SCREEN_WIDTH = 1920; class ResponsiveBox extends _uiCollection_widget.default { _getDefaultOptions() { return _extends({}, super._getDefaultOptions(), { rows: [], cols: [], screenByWidth: null, singleColumnScreen: "", height: "100%", width: "100%", activeStateEnabled: false, focusStateEnabled: false, onLayoutChanged: null }) } _init() { if (!this.option("screenByWidth")) { this._options.silent("screenByWidth", _window.defaultScreenFactorFunc) } super._init(); this._initLayoutChangedAction() } _initLayoutChangedAction() { this._layoutChangedAction = this._createActionByOption("onLayoutChanged", { excludeValidators: ["disabled", "readOnly"] }) } _itemClass() { return "dx-box-item" } _itemDataKey() { return "dxBoxItemData" } _initMarkup() { super._initMarkup(); this.$element().addClass("dx-responsivebox") } _renderItems() { this._setScreenSize(); this._screenItems = this._itemsByScreen(); this._prepareGrid(); this._spreadItems(); this._layoutItems(); this._linkNodeToItem() } _itemOptionChanged(item) { const $item = this._findItemElementByItem(item); if (!$item.length) { return } this._refreshItem($item, item); this._clearItemNodeTemplates(); this._update(true) } _setScreenSize() { const currentScreen = this._getCurrentScreen(); this._removeScreenSizeClass(); this.$element().addClass(SCREEN_SIZE_CLASS_PREFIX + currentScreen); this.option("currentScreenFactor", currentScreen) } _removeScreenSizeClass() { const { currentScreenFactor: currentScreenFactor } = this.option(); if (currentScreenFactor) { this.$element().removeClass(SCREEN_SIZE_CLASS_PREFIX + currentScreenFactor) } } _prepareGrid() { const grid = this._grid = []; this._prepareRowsAndCols(); (0, _iterator.each)(this._rows, (() => { const row = []; grid.push(row); (0, _iterator.each)(this._cols, (() => { row.push(this._createEmptyCell()) })) })) } getSingleColumnRows() { const { rows: rows } = this.option(); const screenItemsLength = this._screenItems.length; if (null !== rows && void 0 !== rows && rows.length) { const filteredRows = this._filterByScreen(rows); const result = []; for (let i = 0; i < screenItemsLength; i++) { const sizeConfig = this._defaultSizeConfig(); if (i < filteredRows.length && (0, _type.isDefined)(filteredRows[i].shrink)) { sizeConfig.shrink = filteredRows[i].shrink } result.push(sizeConfig) } return result } return this._defaultSizeConfig(screenItemsLength) } _prepareRowsAndCols() { if (this._isSingleColumnScreen()) { this._prepareSingleColumnScreenItems(); this._rows = this.getSingleColumnRows(); this._cols = this._defaultSizeConfig(1) } else { this._rows = this._sizesByScreen(this.option("rows")); this._cols = this._sizesByScreen(this.option("cols")) } } _isSingleColumnScreen() { const { singleColumnScreen: singleColumnScreen, rows: rows, cols: cols } = this.option(); return this._screenRegExp().test(singleColumnScreen) || !(null !== rows && void 0 !== rows && rows.length) || !(null !== cols && void 0 !== cols && cols.length) } _prepareSingleColumnScreenItems() { this._screenItems.sort(((item1, item2) => item1.location.row - item2.location.row || item1.location.col - item2.location.col)); (0, _iterator.each)(this._screenItems, ((index, item) => { (0, _extend.extend)(item.location, { row: index, col: 0, rowspan: 1, colspan: 1 }) })) } _sizesByScreen(sizeConfigs) { return (0, _iterator.map)(this._filterByScreen(sizeConfigs), (sizeConfig => (0, _extend.extend)(this._defaultSizeConfig(), sizeConfig))) } _createDefaultSizeConfig() { return { ratio: 1, baseSize: 0, minSize: 0, maxSize: 0 } } _defaultSizeConfig(size) { const defaultSizeConfig = this._createDefaultSizeConfig(); if (!arguments.length) { return defaultSizeConfig } const result = []; for (let i = 0; i < size; i++) { result.push(defaultSizeConfig) } return result } _filterByScreen(items) { const screenRegExp = this._screenRegExp(); return (0, _common.grep)(items, (item => !item.screen || screenRegExp.test(item.screen))) } _screenRegExp() { const screen = this._getCurrentScreen(); return new RegExp(`(^|\\s)${screen}($|\\s)`, "i") } _getCurrentScreen() { const width = this._screenWidth(); const { screenByWidth: screenByWidth } = this.option(); return null === screenByWidth || void 0 === screenByWidth ? void 0 : screenByWidth(width) } _screenWidth() { return (0, _window.hasWindow)() ? (0, _size.getWidth)((0, _window.getWindow)()) : 1920 } _createEmptyCell() { return { item: {}, location: { colspan: 1, rowspan: 1 } } } _spreadItems() { (0, _iterator.each)(this._screenItems, ((_, itemInfo) => { const location = itemInfo.location || {}; const itemCol = location.col; const itemRow = location.row; const row = this._grid[itemRow]; const itemCell = null === row || void 0 === row ? void 0 : row[itemCol]; this._occupyCells(itemCell, itemInfo) })) } _itemsByScreen() { const { items: items } = this.option(); return null === items || void 0 === items ? void 0 : items.reduce(((result, item) => { let locations = item.location || {}; locations = (0, _type.isPlainObject)(locations) ? [locations] : locations; this._filterByScreen(locations).forEach((location => { result.push({ item: item, location: (0, _extend.extend)({ rowspan: 1, colspan: 1 }, location) }) })); return result }), []) } _occupyCells(itemCell, itemInfo) { if (!itemCell || this._isItemCellOccupied(itemCell, itemInfo)) { return }(0, _extend.extend)(itemCell, itemInfo); this._markSpanningCell(itemCell) } _isItemCellOccupied(itemCell, itemInfo) { if (!(0, _type.isEmptyObject)(itemCell.item)) { return true } let result = false; this._loopOverSpanning(itemInfo.location, (cell => { result = result || !(0, _type.isEmptyObject)(cell.item) })); return result } _loopOverSpanning(location, callback) { const rowEnd = location.row + location.rowspan - 1; const colEnd = location.col + location.colspan - 1; const boundRowEnd = Math.min(rowEnd, this._rows.length - 1); const boundColEnd = Math.min(colEnd, this._cols.length - 1); location.rowspan -= rowEnd - boundRowEnd; location.colspan -= colEnd - boundColEnd; for (let rowIndex = location.row; rowIndex <= boundRowEnd; rowIndex++) { for (let colIndex = location.col; colIndex <= boundColEnd; colIndex++) { if (rowIndex !== location.row || colIndex !== location.col) { callback(this._grid[rowIndex][colIndex]) } } } } _markSpanningCell(itemCell) { this._loopOverSpanning(itemCell.location, (cell => { (0, _extend.extend)(cell, { item: itemCell.item, spanningCell: itemCell }) })) } _linkNodeToItem() { (0, _iterator.each)(this._itemElements(), ((_, itemNode) => { const $item = (0, _renderer.default)(itemNode); const item = $item.data("dxBoxItemData"); if (!item.box) { item.node = $item.children() } })) } _layoutItems() { const rowsCount = this._grid.length; const colsCount = rowsCount && this._grid[0].length; if (!rowsCount && !colsCount) { return } const result = this._layoutBlock({ direction: "col", row: { start: 0, end: rowsCount - 1 }, col: { start: 0, end: colsCount - 1 } }); const rootBox = this._prepareBoxConfig(result.box || { direction: "row", items: [(0, _extend.extend)(result, { ratio: 1 })] }); (0, _extend.extend)(rootBox, this._rootBoxConfig(rootBox.items)); this._$root = (0, _renderer.default)("<div>").appendTo(this._itemContainer()); this._createComponent(this._$root, _box.default, rootBox) } _rootBoxConfig(items) { const rootItems = (0, _iterator.each)(items, ((index, item) => { this._needApplyAutoBaseSize(item) && (0, _extend.extend)(item, { baseSize: "auto" }) })); const { itemHoldTimeout: itemHoldTimeout } = this.option(); return { width: "100%", height: "100%", items: rootItems, itemTemplate: this._getTemplateByOption("itemTemplate"), itemHoldTimeout: itemHoldTimeout, onItemHold: this._createActionByOption("onItemHold"), onItemClick: this._createActionByOption("onItemClick"), onItemContextMenu: this._createActionByOption("onItemContextMenu"), onItemRendered: this._createActionByOption("onItemRendered") } } _needApplyAutoBaseSize(item) { return !item.baseSize && (!item.minSize || "auto" === item.minSize) && (!item.maxSize || "auto" === item.maxSize) } _prepareBoxConfig(config) { return (0, _extend.extend)(config || {}, { crossAlign: "stretch", onItemStateChanged: this.option("onItemStateChanged") }) } _layoutBlock(options) { if (this._isSingleItem(options)) { return this._itemByCell(options.row.start, options.col.start) } return this._layoutDirection(options) } _isSingleItem(options) { const firstCellLocation = this._grid[options.row.start][options.col.start].location; const isItemRowSpanned = options.row.end - options.row.start === firstCellLocation.rowspan - 1; const isItemColSpanned = options.col.end - options.col.start === firstCellLocation.colspan - 1; return isItemRowSpanned && isItemColSpanned } _itemByCell(rowIndex, colIndex) { const itemCell = this._grid[rowIndex][colIndex]; return itemCell.spanningCell ? null : itemCell.item } _layoutDirection(options) { const items = []; const { direction: direction } = options; const crossDirection = this._crossDirection(direction); let block; while (block = this._nextBlock(options)) { if (this._isBlockIndivisible(options.prevBlockOptions, block)) { throw _ui.default.Error("E1025") } const item = this._layoutBlock({ direction: crossDirection, row: block.row, col: block.col, prevBlockOptions: options }); if (item) { (0, _extend.extend)(item, this._blockSize(block, crossDirection)); items.push(item) } options[crossDirection].start = block[crossDirection].end + 1 } return { box: this._prepareBoxConfig({ direction: direction, items: items }) } } _isBlockIndivisible(options, block) { return options && options.col.start === block.col.start && options.col.end === block.col.end && options.row.start === block.row.start && options.row.end === block.row.end } _crossDirection(direction) { return "col" === direction ? "row" : "col" } _nextBlock(options) { const { direction: direction } = options; const crossDirection = this._crossDirection(direction); const startIndex = options[direction].start; const endIndex = options[direction].end; const crossStartIndex = options[crossDirection].start; if (crossStartIndex > options[crossDirection].end) { return null } let crossSpan = 1; for (let crossIndex = crossStartIndex; crossIndex < crossStartIndex + crossSpan; crossIndex++) { let lineCrossSpan = 1; for (let index = startIndex; index <= endIndex; index++) { const cell = this._cellByDirection(direction, index, crossIndex); lineCrossSpan = Math.max(lineCrossSpan, cell.location[`${crossDirection}span`]) } const lineCrossEndIndex = crossIndex + lineCrossSpan; const crossEndIndex = crossStartIndex + crossSpan; if (lineCrossEndIndex > crossEndIndex) { crossSpan += lineCrossEndIndex - crossEndIndex } } const result = {}; result[direction] = { start: startIndex, end: endIndex }; result[crossDirection] = { start: crossStartIndex, end: crossStartIndex + crossSpan - 1 }; return result } _cellByDirection(direction, index, crossIndex) { return "col" === direction ? this._grid[crossIndex][index] : this._grid[index][crossIndex] } _blockSize(block, direction) { const defaultMinSize = "row" === direction ? "auto" : 0; const sizeConfigs = "row" === direction ? this._rows : this._cols; const result = (0, _extend.extend)(this._createDefaultSizeConfig(), { ratio: 0 }); for (let index = block[direction].start; index <= block[direction].end; index++) { const sizeConfig = sizeConfigs[index]; result.ratio += sizeConfig.ratio; result.baseSize += sizeConfig.baseSize; result.minSize += sizeConfig.minSize; result.maxSize += sizeConfig.maxSize; if ((0, _type.isDefined)(sizeConfig.shrink)) { result.shrink = sizeConfig.shrink } } result.minSize = result.minSize ? result.minSize : defaultMinSize; result.maxSize = result.maxSize ? result.maxSize : "auto"; this._isSingleColumnScreen() && (result.baseSize = "auto"); return result } _update(forceRemoveRoot) { var _this$_layoutChangedA; const $existingRoot = this._$root; this._renderItems(); if ($existingRoot) { if (forceRemoveRoot) { $existingRoot.remove() } else { $existingRoot.detach(); this._saveAssistantRoot($existingRoot) } } null === (_this$_layoutChangedA = this._layoutChangedAction) || void 0 === _this$_layoutChangedA || _this$_layoutChangedA.call(this) } _saveAssistantRoot($root) { this._assistantRoots = this._assistantRoots || []; this._assistantRoots.push($root) } _dispose() { this._clearItemNodeTemplates(); this._cleanUnusedRoots(); super._dispose.apply(this, arguments) } _cleanUnusedRoots() { if (!this._assistantRoots) { return }(0, _iterator.each)(this._assistantRoots, ((_, item) => { (0, _renderer.default)(item).remove() })) } _clearItemNodeTemplates() { (0, _iterator.each)(this.option("items"), (function() { delete this.node })) } _attachClickEvent() {} _optionChanged(args) { switch (args.name) { case "rows": case "cols": case "screenByWidth": case "singleColumnScreen": this._clearItemNodeTemplates(); this._invalidate(); break; case "width": case "height": super._optionChanged(args); this._update(); break; case "onLayoutChanged": this._initLayoutChangedAction(); break; case "itemTemplate": this._clearItemNodeTemplates(); super._optionChanged(args); break; case "currentScreenFactor": break; default: super._optionChanged(args) } } _dimensionChanged() { if (this._getCurrentScreen() !== this.option("currentScreenFactor")) { this._update() } } repaint() { this._update() } }(0, _component_registrator.default)("dxResponsiveBox", ResponsiveBox); var _default = exports.default = ResponsiveBox;