@egjs/grid
Version:
A component that can arrange items according to the type of grids
1,788 lines (1,515 loc) • 140 kB
JavaScript
/*
Copyright (c) 2021-present NAVER Corp.
name: @egjs/grid
license: MIT
author: NAVER Corp.
repository: https://github.com/naver/egjs-grid
version: 1.18.0
*/
'use strict';
var Component = require('@egjs/component');
var childrenDiffer = require('@egjs/children-differ');
var ImReady = require('@egjs/imready');
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf || {
__proto__: []
} instanceof Array && function (d, b) {
d.__proto__ = b;
} || function (d, b) {
for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p];
};
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function () {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __decorate(decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function __spreadArray(to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) to[j] = from[i];
return to;
}
var DEFAULT_GRID_OPTIONS = {
horizontal: false,
useTransform: false,
percentage: false,
isEqualSize: false,
isConstantSize: false,
gap: 0,
attributePrefix: "data-grid-",
resizeDebounce: 100,
maxResizeDebounce: 0,
autoResize: true,
preserveUIOnDestroy: false,
defaultDirection: "end",
externalContainerManager: null,
externalItemRenderer: null,
renderOnPropertyChange: true,
useFit: true,
outlineLength: 0,
outlineSize: 0,
useRoundedSize: true,
useResizeObserver: false,
observeChildren: false
};
(function (PROPERTY_TYPE) {
PROPERTY_TYPE[PROPERTY_TYPE["PROPERTY"] = 1] = "PROPERTY";
PROPERTY_TYPE[PROPERTY_TYPE["RENDER_PROPERTY"] = 2] = "RENDER_PROPERTY";
})(exports.PROPERTY_TYPE || (exports.PROPERTY_TYPE = {}));
(function (MOUNT_STATE) {
MOUNT_STATE[MOUNT_STATE["UNCHECKED"] = 1] = "UNCHECKED";
MOUNT_STATE[MOUNT_STATE["UNMOUNTED"] = 2] = "UNMOUNTED";
MOUNT_STATE[MOUNT_STATE["MOUNTED"] = 3] = "MOUNTED";
})(exports.MOUNT_STATE || (exports.MOUNT_STATE = {}));
(function (UPDATE_STATE) {
UPDATE_STATE[UPDATE_STATE["NEED_UPDATE"] = 1] = "NEED_UPDATE";
UPDATE_STATE[UPDATE_STATE["WAIT_LOADING"] = 2] = "WAIT_LOADING";
UPDATE_STATE[UPDATE_STATE["UPDATED"] = 3] = "UPDATED";
})(exports.UPDATE_STATE || (exports.UPDATE_STATE = {}));
var GRID_PROPERTY_TYPES = {
gap: exports.PROPERTY_TYPE.RENDER_PROPERTY,
defaultDirection: exports.PROPERTY_TYPE.PROPERTY,
renderOnPropertyChange: exports.PROPERTY_TYPE.PROPERTY,
preserveUIOnDestroy: exports.PROPERTY_TYPE.PROPERTY,
useFit: exports.PROPERTY_TYPE.PROPERTY,
outlineSize: exports.PROPERTY_TYPE.RENDER_PROPERTY,
outlineLength: exports.PROPERTY_TYPE.RENDER_PROPERTY
};
var GRID_METHODS = ["syncElements", "updateItems", "getItems", "setItems", "renderItems", "getContainerInlineSize", "getContainerElement"];
var GRID_EVENTS = ["renderComplete", "contentError"];
var RECT_NAMES = {
horizontal: {
inlinePos: "top",
contentPos: "left",
inlineSize: "height",
contentSize: "width"
},
vertical: {
inlinePos: "left",
contentPos: "top",
inlineSize: "width",
contentSize: "height"
}
};
function getKeys(obj) {
return Object.keys(obj);
}
function getUpdatedItems(items, entries) {
var mountedItems = getMountedItems(items);
return childrenDiffer.diff(entries.map(function (entry) {
return entry.target;
}), mountedItems.map(function (item) {
return item.element;
})).maintained.filter(function (_a) {
var prevIndex = _a[0],
nextIndex = _a[1];
var entrySize = entries[prevIndex].size;
var item = items[nextIndex];
return !item.inlineSize || !item.contentSize || entrySize.inlineSize !== item.computedInlineSize || entrySize.blockSize !== item.computedContentSize;
}).map(function (_a) {
var nextIndex = _a[1];
return items[nextIndex];
});
}
function getMountedItems(items) {
return items.filter(function (item) {
return item.element;
});
}
function getMountedElements(items) {
return getMountedItems(items).map(function (item) {
return item.element;
});
}
function isString(val) {
return typeof val === "string";
}
function isObject(val) {
return typeof val === "object";
}
function isFunction(val) {
return typeof val === "function";
}
function isNumber(val) {
return typeof val === "number";
}
function camelize(str) {
return str.replace(/[\s-_]([a-z])/g, function (all, letter) {
return letter.toUpperCase();
});
}
function sum(arr) {
return arr.reduce(function (a, b) {
return a + b;
}, 0);
}
function getDataAttributes(element, attributePrefix) {
var dataAttributes = {};
var attributes = element.attributes;
var length = attributes.length;
for (var i = 0; i < length; ++i) {
var attribute = attributes[i];
var name = attribute.name,
value = attribute.value;
if (name.indexOf(attributePrefix) === -1) {
continue;
}
dataAttributes[camelize(name.replace(attributePrefix, ""))] = value;
}
return dataAttributes;
}
/* Class Decorator */
function GetterSetter(component) {
var prototype = component.prototype,
propertyTypes = component.propertyTypes;
var _loop_1 = function (name) {
var shouldRender = propertyTypes[name] === exports.PROPERTY_TYPE.RENDER_PROPERTY;
var descriptor = Object.getOwnPropertyDescriptor(prototype, name) || {};
var getter = descriptor.get || function get() {
return this.options[name];
};
var setter = descriptor.set || function set(value) {
var options = this.options;
var prevValue = options[name];
if (prevValue === value) {
return;
}
options[name] = value;
if (shouldRender && options.renderOnPropertyChange) {
this.scheduleRender();
}
};
var attributes = {
enumerable: true,
configurable: true,
get: getter,
set: setter
};
Object.defineProperty(prototype, name, attributes);
};
for (var name in propertyTypes) {
_loop_1(name);
}
}
function withMethods(methods) {
return function (prototype, memberName) {
methods.forEach(function (name) {
if (name in prototype) {
return;
}
prototype[name] = function () {
var _a;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var result = (_a = this[memberName])[name].apply(_a, args); // fix `this` type to return your own `class` instance to the instance using the decorator.
if (result === this[memberName]) {
return this;
} else {
return result;
}
};
});
};
}
function range(length) {
var arr = [];
for (var i = 0; i < length; ++i) {
arr.push(i);
}
return arr;
}
function getRangeCost(value, valueRange) {
return Math.max(value - valueRange[1], valueRange[0] - value, 0) + 1;
}
function between(value, min, max) {
return Math.min(max, Math.max(value, min));
}
function throttle(num, unit) {
if (!unit) {
return num;
}
var reverseUnit = 1 / unit;
return Math.round(num / unit) / reverseUnit;
}
/**
* Decorator that makes the method of grid available in the framework.
* @ko 프레임워크에서 그리드의 메소드를 사용할 수 있게 하는 데코레이터.
* @memberof eg.Grid
* @private
* @example
* ```js
* import { withGridMethods } from "@egjs/grid";
*
* class Grid extends React.Component<Partial<GridProps & GridOptions>> {
* @withGridMethods
* private grid: NativeGrid;
* }
* ```
*/
var withGridMethods = withMethods(GRID_METHODS);
var ResizeWatcher =
/*#__PURE__*/
function () {
function ResizeWatcher(container, options) {
var _this = this;
if (options === void 0) {
options = {};
}
this._resizeTimer = 0;
this._maxResizeDebounceTimer = 0;
this.rect = {
width: 0,
height: 0
};
this._updatedEntries = [];
this._onWindowResize = function () {
_this._scheduleResize([{
target: _this.container
}]);
};
this._onObserve = function (entries) {
var options = _this._options;
var container = _this.container;
var containerRectBox = options.rectBox;
var childrenRectBox = options.childrenRectBox;
_this._scheduleResize(entries.map(function (entry) {
var target = entry.target;
var rectBox = target === container ? containerRectBox : childrenRectBox;
var sizes = rectBox === "border-box" ? entry.borderBoxSize : entry.contentBoxSize; // Safari < 15.3
if (!sizes) {
var contentRect = entry.contentRect;
sizes = [{
inlineSize: contentRect.width,
blockSize: contentRect.height
}];
}
return {
// not array in old browser
size: sizes[0] || sizes,
target: entry.target
};
}));
};
this._scheduleResize = function (entries) {
var _a = _this._options,
resizeDebounce = _a.resizeDebounce,
maxResizeDebounce = _a.maxResizeDebounce;
var updatedEntries = _this._updatedEntries;
updatedEntries.push.apply(updatedEntries, entries);
_this._updatedEntries = updatedEntries.filter(function (entry, index) {
return updatedEntries.lastIndexOf(entry) === index;
});
if (!_this._maxResizeDebounceTimer && maxResizeDebounce >= resizeDebounce) {
_this._maxResizeDebounceTimer = window.setTimeout(_this._onResize, maxResizeDebounce);
}
if (_this._resizeTimer) {
clearTimeout(_this._resizeTimer);
_this._resizeTimer = 0;
}
_this._resizeTimer = window.setTimeout(_this._onResize, resizeDebounce);
};
this._onResize = function () {
clearTimeout(_this._resizeTimer);
clearTimeout(_this._maxResizeDebounceTimer);
_this._maxResizeDebounceTimer = 0;
_this._resizeTimer = 0;
var updated = _this._updatedEntries;
var container = _this.container;
var containerEntry;
var childEntries = updated.filter(function (entry) {
if (entry.target === container) {
containerEntry = entry;
return false;
} else {
return true;
}
});
var isResizeChildren = childEntries.length > 0;
var isResizeContainer = !!containerEntry;
if (isResizeContainer) {
var watchDirection = _this._options.watchDirection;
var prevRect = _this.rect;
var containerEntrySize = containerEntry.size;
if (containerEntrySize) {
// ResizeObserver
_this.setRect({
width: containerEntrySize.inlineSize,
height: containerEntrySize.blockSize
});
} else {
// window's resize event
_this.resize();
}
var rect = _this.rect;
var isWatchWidth = watchDirection === "box" || watchDirection === "width";
var isWatchHeight = watchDirection === "box" || watchDirection === "height";
isResizeContainer = !watchDirection || isWatchWidth && prevRect.width !== rect.width || isWatchHeight && prevRect.height !== rect.height;
}
_this._updatedEntries = [];
if (isResizeContainer || isResizeChildren) {
_this._emitter.trigger("resize", {
isResizeContainer: isResizeContainer,
childEntries: childEntries
});
}
};
this._options = __assign({
resizeDebounce: 100,
maxResizeDebounce: 0,
useResizeObserver: false,
useWindowResize: true,
watchDirection: false,
rectBox: "content-box",
childrenRectBox: "border-box"
}, options);
this.container = isString(container) ? document.querySelector(container) : container;
this._init();
}
var __proto = ResizeWatcher.prototype;
__proto.getRect = function () {
return this.rect;
};
__proto.setRect = function (rect) {
this.rect = __assign({}, rect);
};
__proto.isObserverEnabled = function () {
return !!this._observer;
};
__proto.resize = function () {
var container = this.container;
this.setRect(this._options.rectBox === "border-box" ? {
width: container.offsetWidth,
height: container.offsetHeight
} : {
width: container.clientWidth,
height: container.clientHeight
});
};
__proto.observeChildren = function (children) {
var observer = this._observer;
if (!observer) {
return;
}
var box = this._options.childrenRectBox;
children.forEach(function (element) {
if (element) {
observer.observe(element, {
box: box
});
}
});
};
__proto.unobserveChildren = function (children) {
var observer = this._observer;
if (!observer) {
return;
}
children.forEach(function (element) {
if (element) {
observer.unobserve(element);
}
});
};
__proto.listen = function (callback) {
this._emitter.on("resize", callback);
return this;
};
__proto.destroy = function () {
var _a;
(_a = this._observer) === null || _a === void 0 ? void 0 : _a.disconnect();
if (this._options.useWindowResize) {
window.removeEventListener("resize", this._onWindowResize);
}
};
__proto._init = function () {
var container = this.container;
var options = this._options;
this._emitter = new Component();
if (options.useResizeObserver && !!window.ResizeObserver) {
this._observer = new window.ResizeObserver(this._onObserve);
this._observer.observe(container, {
box: options.rectBox
});
}
if (options.useWindowResize) {
window.addEventListener("resize", this._onWindowResize);
}
this.resize();
};
return ResizeWatcher;
}();
var ContainerManager =
/*#__PURE__*/
function (_super) {
__extends(ContainerManager, _super);
function ContainerManager(container, options) {
var _this = _super.call(this) || this;
_this.container = container;
_this._onResize = function (e) {
_this.trigger("resize", e);
};
_this.options = __assign({
horizontal: DEFAULT_GRID_OPTIONS.horizontal,
autoResize: DEFAULT_GRID_OPTIONS.autoResize,
resizeDebounce: DEFAULT_GRID_OPTIONS.resizeDebounce,
maxResizeDebounce: DEFAULT_GRID_OPTIONS.maxResizeDebounce,
useResizeObserver: DEFAULT_GRID_OPTIONS.useResizeObserver
}, options);
_this._init();
return _this;
}
var __proto = ContainerManager.prototype;
__proto.resize = function () {
var container = this.container;
this.setRect({
width: container.clientWidth,
height: container.clientHeight
});
};
__proto.isObserverEnabled = function () {
return this._watcher.isObserverEnabled();
};
__proto.getRect = function () {
return this._watcher.getRect();
};
__proto.observeChildren = function (children) {
this._watcher.observeChildren(children);
};
__proto.unobserveChildren = function (children) {
this._watcher.unobserveChildren(children);
};
__proto.setRect = function (rect) {
this._watcher.setRect(rect);
};
__proto.getInlineSize = function () {
return this.getRect()[this._names.inlineSize];
};
__proto.getContentSize = function () {
return this.getRect()[this._names.contentSize];
};
__proto.getStatus = function () {
return {
rect: this._watcher.getRect()
};
};
__proto.setStatus = function (status) {
this.setRect(status.rect);
this.setContentSize(this.getContentSize());
};
__proto.setContentSize = function (size) {
var _a;
var sizeName = this.options.horizontal ? "width" : "height";
this.setRect(__assign(__assign({}, this.getRect()), (_a = {}, _a[sizeName] = size, _a)));
this.container.style[sizeName] = size + "px";
};
__proto.destroy = function (options) {
if (options === void 0) {
options = {};
}
this._watcher.destroy();
if (!options.preserveUI) {
this.container.style.cssText = this.orgCSSText;
}
};
__proto._init = function () {
var container = this.container;
var style = window.getComputedStyle(container);
this.orgCSSText = container.style.cssText;
if (style.position === "static") {
container.style.position = "relative";
}
var options = this.options;
this._watcher = new ResizeWatcher(container, {
useWindowResize: options.autoResize,
useResizeObserver: options.useResizeObserver,
resizeDebounce: options.resizeDebounce,
maxResizeDebounce: options.maxResizeDebounce,
watchDirection: options.useResizeObserver ? this._names.inlineSize : false
}).listen(this._onResize);
};
Object.defineProperty(__proto, "_names", {
get: function () {
return RECT_NAMES[this.options.horizontal ? "horizontal" : "vertical"];
},
enumerable: false,
configurable: true
});
return ContainerManager;
}(Component);
var ItemRenderer =
/*#__PURE__*/
function () {
function ItemRenderer(options) {
this.initialRects = {};
this.sizePercetage = false;
this.posPercetage = false;
this.options = __assign({
attributePrefix: DEFAULT_GRID_OPTIONS.attributePrefix,
useTransform: DEFAULT_GRID_OPTIONS.useTransform,
horizontal: DEFAULT_GRID_OPTIONS.horizontal,
percentage: DEFAULT_GRID_OPTIONS.percentage,
isEqualSize: DEFAULT_GRID_OPTIONS.isEqualSize,
isConstantSize: DEFAULT_GRID_OPTIONS.isConstantSize,
useRoundedSize: DEFAULT_GRID_OPTIONS.useRoundedSize
}, options);
this._init();
}
var __proto = ItemRenderer.prototype;
__proto.resize = function () {
this.initialRects = {};
};
__proto.renderItems = function (items) {
var _this = this;
items.forEach(function (item) {
_this._renderItem(item);
});
};
__proto.getInlineSize = function () {
return this.containerRect[this.options.horizontal ? "height" : "width"];
};
__proto.setContainerRect = function (rect) {
this.containerRect = rect;
};
__proto.updateEqualSizeItems = function (items, totalItems) {
var _this = this;
this.updateItems(items);
var hasSizeGroup = items.some(function (item) {
return item.attributes.sizeGroup;
}); // Check the rest of the items(totalItems) except `items`.
if (this.options.isEqualSize || hasSizeGroup) {
var updatedItem = items.some(function (item) {
return item.updateState === exports.UPDATE_STATE.UPDATED;
});
if (updatedItem) {
totalItems.forEach(function (item) {
if (items.indexOf(item) === -1) {
_this.updateItem(item, true);
}
});
}
}
};
__proto.updateItems = function (items) {
var _this = this;
items.forEach(function (item) {
_this.updateItem(item);
});
};
__proto.getStatus = function () {
return {
initialRects: this.initialRects
};
};
__proto.setStatus = function (status) {
this.initialRects = status.initialRects;
};
__proto._init = function () {
var percentage = this.options.percentage;
var sizePercentage = false;
var posPercentage = false;
if (percentage === true) {
sizePercentage = true;
posPercentage = true;
} else if (percentage) {
if (percentage.indexOf("position") > -1) {
posPercentage = true;
}
if (percentage.indexOf("size") > -1) {
sizePercentage = true;
}
}
this.posPercetage = posPercentage;
this.sizePercetage = sizePercentage;
};
__proto.updateItem = function (item, checkSizeGroup) {
var _a;
var _b = this.options,
isEqualSize = _b.isEqualSize,
isConstantSize = _b.isConstantSize,
useRoundedSize = _b.useRoundedSize;
var initialRects = this.initialRects;
var orgRect = item.orgRect,
element = item.element;
var isLoading = item.updateState === exports.UPDATE_STATE.WAIT_LOADING;
var hasOrgSize = orgRect && orgRect.width && orgRect.height;
var rect;
var attributes = element ? getDataAttributes(element, this.options.attributePrefix) : item.attributes;
var sizeGroup = (_a = attributes.sizeGroup) !== null && _a !== void 0 ? _a : "";
var isNotEqualSize = attributes.notEqualSize;
if (sizeGroup !== "" && initialRects[sizeGroup]) {
rect = initialRects[sizeGroup];
} else if (isEqualSize && !isNotEqualSize && !sizeGroup && initialRects[""]) {
rect = initialRects[""];
} else if (isConstantSize && hasOrgSize && !isLoading && item.isFirstUpdate) {
rect = orgRect;
} else if (checkSizeGroup || !element) {
return;
} else {
rect = {
left: element.offsetLeft,
top: element.offsetTop,
width: 0,
height: 0
};
if (useRoundedSize) {
rect.width = element.offsetWidth;
rect.height = element.offsetHeight;
} else {
var clientRect = element.getBoundingClientRect();
rect.width = clientRect.width;
rect.height = clientRect.height;
}
}
item.attributes = attributes;
item.shouldReupdate = false;
if (!item.isFirstUpdate || !hasOrgSize) {
item.orgRect = __assign({}, rect);
}
item.rect = __assign({}, rect); // If it's equal size items, it doesn't affect the state.
if (!checkSizeGroup) {
if (item.element) {
item.mountState = exports.MOUNT_STATE.MOUNTED;
}
if (item.updateState === exports.UPDATE_STATE.NEED_UPDATE) {
item.updateState = exports.UPDATE_STATE.UPDATED;
item.isFirstUpdate = true;
}
if (!isLoading && !isNotEqualSize && !initialRects[sizeGroup]) {
initialRects[sizeGroup] = __assign({}, rect);
}
}
return rect;
};
__proto._renderItem = function (item) {
var element = item.element;
var cssRect = item.cssRect;
if (!element || !cssRect) {
return;
}
var _a = this.options,
horizontal = _a.horizontal,
useTransform = _a.useTransform;
var posPercentage = this.posPercetage;
var sizePercentage = this.sizePercetage;
var cssTexts = ["position: absolute;"];
var _b = RECT_NAMES[horizontal ? "horizontal" : "vertical"],
sizeName = _b.inlineSize,
posName = _b.inlinePos;
var inlineSize = this.getInlineSize();
var keys = getKeys(cssRect);
var hasRectProperties = keys.length > 0;
if (useTransform) {
keys = keys.filter(function (key) {
return key !== "top" && key !== "left";
});
cssTexts.push("transform: " + ("translate(" + (cssRect.left || 0) + "px, " + (cssRect.top || 0) + "px);"));
}
cssTexts.push.apply(cssTexts, keys.map(function (name) {
var value = cssRect[name];
if (name === sizeName && sizePercentage || name === posName && posPercentage) {
return name + ": " + value / inlineSize * 100 + "%;";
}
return name + ": " + value + "px;";
}));
if (hasRectProperties) {
element.style.cssText += cssTexts.join("");
}
};
return ItemRenderer;
}();
/**
* @memberof Grid
* @implements Grid.GridItem.GridItemStatus
*/
var GridItem =
/*#__PURE__*/
function () {
/**
* @constructor
* @param horizontal - Direction of the scroll movement. (true: horizontal, false: vertical) <ko>스크롤 이동 방향. (true: 가로방향, false: 세로방향)</ko>
* @param itemStatus - Default status object of GridItem module. <ko>GridItem 모듈의 기본 status 객체.</ko>
*/
function GridItem(horizontal, itemStatus) {
if (itemStatus === void 0) {
itemStatus = {};
}
var _a;
this.horizontal = horizontal;
/**
* Whether or not it will be updated upon request.
* @inner
*/
this.isUpdating = false;
/**
* Whether the item needs to be updated again
* @inner
*/
this.shouldReupdate = false;
this.hasTransition = false;
this.transitionDuration = "";
this.isRestoreOrgCSSText = true;
var element = itemStatus.element;
var status = __assign({
key: "",
index: 0,
orgRect: {
left: 0,
top: 0,
width: 0,
height: 0
},
rect: {
left: 0,
top: 0,
width: 0,
height: 0
},
cssRect: {},
attributes: {},
data: {},
isFirstUpdate: false,
mountState: exports.MOUNT_STATE.UNCHECKED,
updateState: exports.UPDATE_STATE.NEED_UPDATE,
element: element || null,
orgCSSText: (_a = element === null || element === void 0 ? void 0 : element.style.cssText) !== null && _a !== void 0 ? _a : "",
gridData: {}
}, itemStatus);
for (var name in status) {
this[name] = status[name];
}
}
var __proto = GridItem.prototype;
Object.defineProperty(__proto, "orgInlineSize", {
/**
* The size in inline direction before first rendering. "width" if horizontal is false, "height" otherwise.
* @ko 첫 렌더링 되기 전의 inline 방향의 사이즈. horizontal이 false면 "width", 아니면 "height".
* @member Grid.GridItem#orgInlineSize
*/
get: function () {
var name = this._names.inlineSize;
return this.orgRect[name] || this.rect[name];
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "orgContentSize", {
/**
* The size in content direction before first rendering. "height" if horizontal is false, "width" otherwise.
* @ko 첫 렌더링 되기 전의 content 방향의 사이즈. horizontal이 false면 "height", 아니면 "width".
* @member Grid.GridItem#orgContentSize
*/
get: function () {
var name = this._names.contentSize;
return this.orgRect[name] || this.rect[name];
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "inlineSize", {
/**
* The size in inline direction. "width" if horizontal is false, "height" otherwise.
* @ko inline 방향의 사이즈. horizontal이 false면 "width", 아니면 "height".
* @member Grid.GridItem#inlineSize
*/
get: function () {
return this.rect[this._names.inlineSize];
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "contentSize", {
/**
* The size in content direction. "height" if horizontal is false, "width" otherwise.
* @ko content 방향의 사이즈. horizontal이 false면 "height", 아니면 "width".
* @member Grid.GridItem#contentSize
*/
get: function () {
return this.rect[this._names.contentSize];
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "cssInlineSize", {
/**
* The CSS size in inline direction applied to the Grid. "width" if horizontal is false, "height" otherwise.
* @ko Grid에 적용된 inline 방향의 CSS 사이즈. horizontal이 false면 "width", 아니면 "height".
* @member Grid.GridItem#cssInlineSize
*/
get: function () {
return this.cssRect[this._names.inlineSize];
},
set: function (inlineSize) {
this.cssRect[this._names.inlineSize] = inlineSize;
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "cssContentSize", {
/**
* The CSS size in content direction applied to the Grid. "height" if horizontal is false, "width" otherwise.
* @ko Grid에 적용된 content 방향의 CSS 사이즈. horizontal이 false면 "height", 아니면 "width".
* @member Grid.GridItem#cssContentSize
*/
get: function () {
return this.cssRect[this._names.contentSize];
},
set: function (contentSize) {
this.cssRect[this._names.contentSize] = contentSize;
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "cssInlinePos", {
/**
* The CSS pos in inline direction applied to the Grid. "left" if horizontal is false, "top" otherwise.
* @ko Grid에 적용된 inline 방향의 CSS 포지션. horizontal이 false면 "left", 아니면 "top".
* @member Grid.GridItem#cssInlinePos
*/
get: function () {
return this.cssRect[this._names.inlinePos];
},
set: function (inlinePos) {
this.cssRect[this._names.inlinePos] = inlinePos;
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "cssContentPos", {
/**
* The CSS pos in content direction applied to the Grid. "top" if horizontal is false, "left" otherwise.
* @ko Grid에 적용된 content 방향의 CSS 포지션. horizontal이 false면 "top", 아니면 "left".
* @member Grid.GridItem#cssContentPos
*/
get: function () {
return this.cssRect[this._names.contentPos];
},
set: function (contentPos) {
this.cssRect[this._names.contentPos] = contentPos;
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "computedInlineSize", {
/**
* Calculated size in the direction of the inline applied to the grid. "width" if horizontal is false, "height" otherwise.
* @ko Grid에 적용된 inline 방향의 계산된 사이즈. horizontal이 false면 "width", 아니면 "height".
* @member Grid.GridItem#computedInlineSize
*/
get: function () {
var name = this._names.inlineSize;
return this.cssRect[name] || this.rect[name] || this.orgRect[name];
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "computedContentSize", {
/**
* Calculated size in the direction of the content applied to the grid. "height" if horizontal is false, "width" otherwise.
* @ko Grid에 적용된 content 방향의 계산된 사이즈. horizontal이 false면 "height", 아니면 "width".
* @member Grid.GridItem#computedContentSize
*/
get: function () {
var name = this._names.contentSize;
return this.cssRect[name] || this.rect[name] || this.orgRect[name];
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "computedInlinePos", {
/**
* Calculated position in the direction of the inline applied to the grid. "left" if horizontal is false, "top" otherwise.
* @ko Grid에 적용된 content 방향의 계산된 포지션. horizontal이 false면 "left", 아니면 "top".
* @member Grid.GridItem#computedInlinePos
*/
get: function () {
var _a;
var name = this._names.inlinePos;
return (_a = this.cssRect[name]) !== null && _a !== void 0 ? _a : this.rect[name];
},
enumerable: false,
configurable: true
});
Object.defineProperty(__proto, "computedContentPos", {
/**
* Calculated position in the direction of the content applied to the grid. "top" if horizontal is false, "left" otherwise.
* @ko Grid에 적용된 content 방향의 계산된 포지션. horizontal이 false면 "top", 아니면 "left".
* @member Grid.GridItem#computedContentPos
*/
get: function () {
var _a;
var name = this._names.contentPos;
return (_a = this.cssRect[name]) !== null && _a !== void 0 ? _a : this.rect[name];
},
enumerable: false,
configurable: true
});
/**
* Set CSS rect through GridRect.
* @ko GridRect을 통해 CSS Rect를 설정한다.
* @param - The style for setting CSS rect. <ko>CSS rect를 설정하기 위한 스타일.</ko>
*/
__proto.setCSSGridRect = function (gridRect) {
var names = RECT_NAMES[this.horizontal ? "horizontal" : "vertical"];
var rect = {};
for (var name in gridRect) {
rect[names[name]] = gridRect[name];
}
this.cssRect = rect;
};
/**
* Add CSS part of rect through GridRect.
* @ko GridRect을 통해 CSS Rect의 일부를 추가한다..
* @param - The style for setting CSS rect. <ko>CSS rect를 설정하기 위한 스타일.</ko>
*/
__proto.addCSSGridRect = function (gridRect) {
var names = RECT_NAMES[this.horizontal ? "horizontal" : "vertical"];
var rect = this.cssRect;
for (var name in gridRect) {
rect[names[name]] = gridRect[name];
}
};
/**
* Returns the status of the item.
* @ko 아이템의 상태를 반환한다.
*/
__proto.getStatus = function () {
return {
index: this.index,
mountState: this.mountState,
updateState: this.updateState,
attributes: this.attributes,
orgCSSText: this.orgCSSText,
isFirstUpdate: this.isFirstUpdate,
element: null,
key: this.key,
orgRect: this.orgRect,
rect: this.rect,
cssRect: this.cssRect,
gridData: this.gridData,
data: this.data
};
};
/**
* Returns minimized status of the item.
* @ko 아이템의 간소화된 상태를 반환한다.
*/
__proto.getMinimizedStatus = function () {
var status = {
orgRect: this.orgRect,
rect: this.rect,
cssRect: this.cssRect,
attributes: this.attributes,
gridData: this.gridData
};
var _a = this,
key = _a.key,
mountState = _a.mountState,
updateState = _a.updateState,
isFirstUpdate = _a.isFirstUpdate,
orgCSSText = _a.orgCSSText;
if (typeof key !== "undefined") {
status.key = key;
}
if (mountState !== exports.MOUNT_STATE.UNCHECKED) {
status.mountState = mountState;
}
if (updateState !== exports.UPDATE_STATE.NEED_UPDATE) {
status.updateState = updateState;
}
if (isFirstUpdate) {
status.isFirstUpdate = true;
}
if (orgCSSText) {
status.orgCSSText = orgCSSText;
}
return status;
};
Object.defineProperty(__proto, "_names", {
get: function () {
return this.horizontal ? RECT_NAMES.horizontal : RECT_NAMES.vertical;
},
enumerable: false,
configurable: true
});
return GridItem;
}();
/**
* @extends eg.Component
*/
var Grid =
/*#__PURE__*/
function (_super) {
__extends(Grid, _super);
/**
* @param - A base element for a module <ko>모듈을 적용할 기준 엘리먼트</ko>
* @param - The option object of the Grid module <ko>Grid 모듈의 옵션 객체</ko>
*/
function Grid(containerElement, options) {
if (options === void 0) {
options = {};
}
var _this = _super.call(this) || this;
_this.items = [];
_this.outlines = {
start: [],
end: []
};
_this._renderTimer = 0;
_this._onResize = function (e) {
if (e.isResizeContainer) {
_this._renderItems({
useResize: true
}, true);
} else {
var updatedItems = getUpdatedItems(_this.items, e.childEntries);
if (updatedItems.length > 0) {
_this.updateItems(updatedItems);
}
}
};
_this.options = __assign(__assign({}, _this.constructor.defaultOptions), options);
_this.containerElement = isString(containerElement) ? document.querySelector(containerElement) : containerElement;
var _a = _this.options,
isEqualSize = _a.isEqualSize,
isConstantSize = _a.isConstantSize,
useTransform = _a.useTransform,
horizontal = _a.horizontal,
percentage = _a.percentage,
externalContainerManager = _a.externalContainerManager,
externalItemRenderer = _a.externalItemRenderer,
resizeDebounce = _a.resizeDebounce,
maxResizeDebounce = _a.maxResizeDebounce,
autoResize = _a.autoResize,
useRoundedSize = _a.useRoundedSize,
useResizeObserver = _a.useResizeObserver; // TODO: 테스트용 설정
_this.containerManager = externalContainerManager || new ContainerManager(_this.containerElement, {
horizontal: horizontal,
resizeDebounce: resizeDebounce,
maxResizeDebounce: maxResizeDebounce,
autoResize: autoResize,
useResizeObserver: useResizeObserver
}).on("resize", _this._onResize);
_this.itemRenderer = externalItemRenderer || new ItemRenderer({
useTransform: useTransform,
isEqualSize: isEqualSize,
isConstantSize: isConstantSize,
percentage: percentage,
useRoundedSize: useRoundedSize
});
_this._init();
return _this;
}
var __proto = Grid.prototype;
Grid_1 = Grid;
/**
* Return Container Element.
* @ko 컨테이너 엘리먼트를 반환한다.
*/
__proto.getContainerElement = function () {
return this.containerElement;
};
/**
* Return items.
* @ko 아이템들을 반환한다.
*/
__proto.getItems = function () {
return this.items;
};
/**
* Returns the children of the container element.
* @ko 컨테이너 엘리먼트의 children을 반환한다.
*/
__proto.getChildren = function () {
return [].slice.call(this.containerElement.children);
};
/**
* Set items.
* @ko 아이템들을 설정한다.
* @param items - The items to set. <ko>설정할 아이템들</ko>
*/
__proto.setItems = function (items) {
items.forEach(function (item, i) {
item.index = i;
});
var options = this.options;
if (options.useResizeObserver && options.observeChildren) {
var containerManager = this.containerManager;
containerManager.unobserveChildren(getMountedElements(this.items));
containerManager.observeChildren(getMountedElements(items));
}
this.items = items;
return this;
};
/**
* Gets the container's inline size. ("width" if horizontal is false, otherwise "height")
* @ko container의 inline 사이즈를 가져온다. (horizontal이 false면 "width", 아니면 "height")
*/
__proto.getContainerInlineSize = function () {
return this.containerManager.getInlineSize();
};
/**
* Returns the outlines of the start and end of the Grid.
* @ko Grid의 처음과 끝의 outline을 반환한다.
*/
__proto.getOutlines = function () {
return this.outlines;
};
/**
* Set outlines.
* @ko 아웃라인을 설정한다.
* @param outlines - The outlines to set. <ko>설정할 아웃라인.</ko>
*/
__proto.setOutlines = function (outlines) {
this.outlines = outlines;
return this;
};
/**
* When elements change, it synchronizes and renders items.
* @ko elements가 바뀐 경우 동기화를 하고 렌더링을 한다.
* @param - Options for rendering. <ko>렌더링을 하기 위한 옵션.</ko>
*/
__proto.syncElements = function (options) {
if (options === void 0) {
options = {};
}
var items = this.items;
var horizontal = this.options.horizontal;
var elements = this.getChildren();
var _a = childrenDiffer.diff(this.items.map(function (item) {
return item.element;
}), elements),
added = _a.added,
maintained = _a.maintained,
changed = _a.changed,
removed = _a.removed;
var nextItems = [];
maintained.forEach(function (_a) {
var beforeIndex = _a[0],
afterIndex = _a[1];
nextItems[afterIndex] = items[beforeIndex];
});
added.forEach(function (index) {
nextItems[index] = new GridItem(horizontal, {
element: elements[index]
});
});
this.setItems(nextItems);
if (added.length || removed.length || changed.length) {
this.renderItems(options);
}
return this;
};
/**
* Update the size of the items and render them.
* @ko 아이템들의 사이즈를 업데이트하고 렌더링을 한다.
* @param - Items to be updated. <ko>업데이트할 아이템들.</ko>
* @param - Options for rendering. <ko>렌더링을 하기 위한 옵션.</ko>
*/
__proto.updateItems = function (items, options) {
if (items === void 0) {
items = this.items;
}
if (options === void 0) {
options = {};
}
var useOrgResize = options.useOrgResize;
items.forEach(function (item) {
if (useOrgResize) {
var orgRect = item.orgRect;
orgRect.width = 0;
orgRect.height = 0;
}
item.updateState = exports.UPDATE_STATE.NEED_UPDATE;
});
this.checkReady(options);
return this;
};
/**
* Rearrange items to fit the grid and render them. When rearrange is complete, the `renderComplete` event is fired.
* @ko grid에 맞게 아이템을 재배치하고 렌더링을 한다. 배치가 완료되면 `renderComplete` 이벤트가 발생한다.
* @param - Options for rendering. <ko>렌더링을 하기 위한 옵션.</ko>
* @example
* ```js
* import { MasonryGrid } from "@egjs/grid";
* const grid = new MasonryGrid();
*
* grid.on("renderComplete", e => {
* console.log(e);
* });
* grid.renderItems();
* ```
*/
__proto.renderItems = function (options) {
if (options === void 0) {
options = {};
}
this._renderItems(options);
return this;
};
/**
* Returns current status such as item's position, size. The returned status can be restored with the setStatus() method.
* @ko 아이템의 위치, 사이즈 등 현재 상태를 반환한다. 반환한 상태는 setStatus() 메서드로 복원할 수 있다.
* @param - Whether to minimize the status of the item. (default: false) <ko>item의 status를 최소화할지 여부. (default: false)</ko>
*/
__proto.getStatus = function (minimize) {
return {
outlines: this.outlines,
items: this.items.map(function (item) {
return minimize ? item.getMinimizedStatus() : item.getStatus();
}),
containerManager: this.containerManager.getStatus(),
itemRenderer: this.itemRenderer.getStatus()
};
};
/**
* Set status of the Grid module with the status returned through a call to the getStatus() method.
* @ko getStatus() 메서드에 대한 호출을 통해 반환된 상태로 Grid 모듈의 상태를 설정한다.
*/
__proto.setStatus = function (status) {
var _this = this;
var horizontal = this.options.horizontal;
var containerManager = this.containerManager;
var prevInlineSize = containerManager.getInlineSize();
var children = this.getChildren();
this.itemRenderer.setStatus(status.itemRenderer);
containerManager.setStatus(status.containerManager);
this.outlines = status.outlines;
this.items = status.items.map(function (item, i) {
return new GridItem(horizontal, __assign(__assign({}, item), {
element: children[i]
}));
});
this.itemRenderer.renderItems(this.items);
if (prevInlineSize !== containerManager.getInlineSize()) {
this.renderItems({
useResize: true
});
} else {
window.setTimeout(function () {
_this._renderComplete({
direction: _this.defaultDirection,
mounted: _this.items,
updated: [],
isResize: false
});
});
}
return this;
};
/**
* Get the inline size corresponding to outline.
* @ko outline에 해당하는 inline 사이즈를 구한다.
* @param items - Items to get outline size. <ko>outline 사이즈를 구하기 위한 아이템들.</ko>
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
__proto.getComputedOutlineSize = function (items) {
if (items === void 0) {
items = this.items;
}
return this.options.outlineSize || this.getContainerInlineSize();
};
/**
* Get the length corresponding to outline.
* @ko outline에 해당하는 length를 가져온다.
* @param items - Items to get outline length. <ko>outline length를 구하기 위한 아이템들.</ko>
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
__proto.getComputedOutlineLength = function (items) {
if (items === void 0) {
items = this.items;
}
return this.options.outlineLength || 1;
};
/**
* Releases the instnace and events and returns the CSS of the container and elements.
* @ko 인스턴스와 이벤트를 해제하고 컨테이너와 엘리먼트들의 CSS를 되돌린다.
* @param Options for destroy. <ko>destory()를 위한 옵션</ko>
*/
__proto.destroy = function (options) {
var _a;
if (options === void 0) {
options = {};
}
var _b = options.preserveUI,
preserveUI = _b === void 0 ? this.options.preserveUIOnDestroy : _b;
this.containerManager.destroy({
preserveUI: preserveUI
});
if (!preserveUI) {
this.items.forEach(function (_a) {
var element = _a.element,
orgCSSText = _a.orgCSSText;
if (element) {
element.style.cssText = orgCSSText;
}
});
}
(_a = this._im) === null || _a === void 0 ? void 0 : _a.destroy();
};
__proto.getInlineGap = function () {
return this._getDirectionalGap('inline');
};
__proto.getContentGap = function () {
return this._getDirectionalGap('content');
};
__proto.checkReady = function (options) {
var _this = this;
var _a;
if (options === void 0) {
options = {};
} // Grid: renderItems => checkReady => readyItems => applyGrid
var useOrgResize = options.useOrgResize;
var items = this.items;
var updated = items.filter(function (item) {
var _a;
return ((_a = item.element) === null || _a === void 0 ? void 0 : _a.parentNode) && item.updateState !== exports.UPDATE_STATE.UPDATED;
});
var mounted = items.filter(function (item) {
var _a;
return ((_a = item.element) === null || _a === void 0 ? void 0 : _a.parentNode) && item.mountState !== exports.MOUNT_STATE.MOUNTED;
});
var moreUpdated = [];
mounted.filter(function (item) {
if (item.hasTransition) {
return true;
} else {
var element = item.element;
var transitionDuration = parseFloat(getComputedStyle(element).transitionDuration);
if (transitionDuration > 0) {
item.hasTransition = true;
item.transitionDuration = element.style.transitionDuration;
return true;
}
}
return false;
}).forEach(function (item) {
item.element.style.transitionDuration = "0s";
});
(_a = this._im) === null || _a === void 0 ? void 0 : _a.destroy();
this._im = new ImReady({
prefix: this.options.attributePrefix
}).on("preReadyElement", function (e) {
updated[e.index].updateState = exports.UPDATE_STATE.WAIT_LOADING;
}).on("preReady", function () {
// reset org size
updated.forEach(function (item) {
var isInitialState = !item.isFirstUpdate || !item.orgRect.width || !item.orgRect.height;
var hasCSSSize = item.cssRect.width || item.cssRect.height;
if ((isInitialState || useOrgResize) && hasCSSSize) {
item.element.style.cssText = item.orgCSSText;
}
});
_this._updateItems(updated);
_this.readyItems(mounted, updated, options);
}).on("readyElement", function (e) {
var item = updated[e.index];
item.updateState = exports.UPDATE_STATE.NEED_UPDATE; // after preReady
if (e.isPreReadyOver) {
var isInitialState = !item.isFirstUpdate || !item.orgRect.width || !item.orgRect.height;
var hasCSSSize = item.cssRect.width || item.cssRect.height;
if (item.isRestoreOrgCSSText || (isInitialState || useOrgResize) && hasCSSSize) {
item.element.style.cssText = item.orgCSSText;
}
_this._updateItems([item]);
_this.readyItems([], [item], options);
}
}).on("error", function (e) {
var item = updated[e.index];
/**
* This event is fired when an error occurs in the content.
* @ko 콘텐츠 로드에 에러가 날 때 발생하는 이벤트.
* @event Grid#contentError
* @param {Grid.OnContentError} e - The object of data to be sent to an event <ko>이벤트에 전달되는 데이터 객체</ko>
* @example
* ```js
* grid.on("contentError", e => {
* e.update();
* });
* ```
*/
_this.trigger("contentError", {
element: e.element,
target: e.target,
item: item,
update: function () {
moreUpdated.push(item);
}
});
}).on("ready", function () {
if (moreUpdated.length) {
_this.updateItems(moreUpdated);
}
}).check(updated.map(function (item) {
return item.element;
}));
};
__proto.scheduleRender = function () {
var _this = this;
this._clearRenderTimer();
this._renderTimer = window.setTimeout(function () {
_this.renderItems();
});
};
__proto.fitOutlines = function (useFit) {
if (useFit === void 0) {
useFit = this.useFit;
}
var outlines = this.outlines;
var startOutline = outlines.start;
var endOutline = outlines.end;
var outlineOffset = startOutline.length ? Math.min.apply(Math, startOutline) : 0; // If the outline is less than 0, a fit occurs forcibly.
if (!useFit && outlineOffset > 0) {
return;
}
outlines.start = startOutline.map(function (point) {
return point - outlineOffset;
});
outlines.end = endOutline.map(function (point) {
return point - outlineOffset;
});
this.items.forEach(function (item) {
var contentPos = item.cssContentPos;
if (!isNumber(contentPos)) {
return;
}
item.cssContentPos = contentPos - outlineOffset;
});
};
__proto.readyItems = function (mounted, updated, options) {
var prevOutlines = this.outlines;
var direction = options.direction || this.options.defaultDirecti