-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathutils.ts
168 lines (150 loc) · 4.77 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* 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 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);