@egjs/grid
Version:
A component that can arrange items according to the type of grids
172 lines (153 loc) • 4.98 kB
text/typescript
/**
* egjs-grid
* Copyright (c) 2021-present NAVER Corp.
* MIT license
*/
import Grid from "./Grid";
import { GRID_METHODS, GRID_PROPERTY_TYPES, PROPERTY_TYPE } from "./consts";
import { GridItem } from "./GridItem";
import { ResizeWatcherEntry } from "./ResizeWatcher";
import { diff } from "@egjs/children-differ";
export function getKeys<T extends Record<string, any>>(obj: T): Array<keyof T> {
return Object.keys(obj);
}
export function getUpdatedItems(items: GridItem[], entries: ResizeWatcherEntry[]) {
const mountedItems = getMountedItems(items);
return diff(
entries.map((entry) => entry.target),
mountedItems.map((item) => item.element!),
).maintained.filter(([prevIndex, nextIndex]) => {
const entrySize = entries[prevIndex].size!;
const item = items[nextIndex];
return !item.inlineSize || !item.contentSize
|| entrySize.inlineSize !== item.computedInlineSize
|| entrySize.blockSize !== item.computedContentSize;
}).map(([, nextIndex]) => items[nextIndex]);
}
export function getMountedItems(items: GridItem[]) {
return items.filter((item) => item.element);
}
export function getMountedElements(items: GridItem[]) {
return getMountedItems(items).map((item) => item.element!);
}
export function isString(val: any): val is string {
return typeof val === "string";
}
export function isObject(val: any): val is object {
return typeof val === "object";
}
export function isFunction(val: any): val is Function {
return typeof val === "function";
}
export function isNumber(val: any): val is number {
return typeof val === "number";
}
export function camelize(str: string) {
return str.replace(/[\s-_]([a-z])/g, (all, letter) => letter.toUpperCase());
}
export function sum(arr: number[]) {
return arr.reduce((a, b) => a + b, 0);
}
export function getDataAttributes(element: HTMLElement, attributePrefix: string) {
const dataAttributes: Record<string, string> = {};
const attributes = element.attributes;
const length = attributes.length;
for (let i = 0; i < length; ++i) {
const attribute = attributes[i];
const { name, value } = attribute;
if (name.indexOf(attributePrefix) === -1) {
continue;
}
dataAttributes[camelize(name.replace(attributePrefix, ""))] = value;
}
return dataAttributes;
}
/* Class Decorator */
export function GetterSetter(component: {
prototype: Grid<any>,
propertyTypes: typeof GRID_PROPERTY_TYPES,
}) {
const {
prototype,
propertyTypes,
} = component;
for (const name in propertyTypes) {
const shouldRender = propertyTypes[name] === PROPERTY_TYPE.RENDER_PROPERTY;
const descriptor = Object.getOwnPropertyDescriptor(prototype, name) || {};
const getter = descriptor.get || function get(this: Grid) {
return this.options[name];
};
const setter = descriptor.set || function set(this: Grid, value: any) {
const options = this.options;
const prevValue = options[name];
if (prevValue === value) {
return;
}
options[name] = value;
if (shouldRender && options.renderOnPropertyChange) {
this.scheduleRender();
}
};
const attributes: Record<string, any> = {
enumerable: true,
configurable: true,
get: getter,
set: setter,
};
Object.defineProperty(prototype, name, attributes);
}
}
export function withMethods(methods: readonly string[]) {
return function (prototype: any, memberName: string) {
methods.forEach((name: string) => {
if (name in prototype) {
return;
}
prototype[name] = function (...args) {
const result = this[memberName][name](...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;
}
};
});
};
}
export function range(length: number): number[] {
const arr: number[] = [];
for (let i = 0; i < length; ++i) {
arr.push(i);
}
return arr;
}
export function getRangeCost(value: number, valueRange: number[]) {
return Math.max(value - valueRange[1], valueRange[0] - value, 0) + 1;
}
export function between(value: number, min: number, max: number) {
return Math.min(max, Math.max(value, min));
}
export function throttle(num: number, unit?: number) {
if (!unit) {
return num;
}
const 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;
* }
* ```
*/
export const withGridMethods = withMethods(GRID_METHODS);