@central-design-system/components 3.0.0-alpha.3 → 3.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/scss/themes/index.ts +1 -1
- package/src/utils/__test__/helper.test.ts +59 -0
- package/src/utils/__test__/mocks/helper.mock.ts +362 -0
- package/src/utils/__test__/mocks/propFactory.mock.ts +88 -0
- package/src/utils/__test__/propFactory.test.ts +9 -0
- package/src/utils/anchor.ts +76 -0
- package/src/utils/animation.ts +62 -0
- package/src/utils/box.ts +39 -0
- package/src/utils/cache.ts +12 -0
- package/src/utils/changeCase/__test__/lowerCase.test.ts +12 -0
- package/src/utils/changeCase/__test__/mocks/lowerCase.mock.ts +80 -0
- package/src/utils/changeCase/__test__/mocks/noCase.mock.ts +131 -0
- package/src/utils/changeCase/__test__/mocks/paramCase.mock.ts +39 -0
- package/src/utils/changeCase/__test__/mocks/pascalCase.mock.ts +46 -0
- package/src/utils/changeCase/__test__/noCase.test.ts +8 -0
- package/src/utils/changeCase/__test__/paramCase.test.ts +8 -0
- package/src/utils/changeCase/__test__/pascalCase.test.ts +8 -0
- package/src/utils/changeCase/lowerCase.ts +56 -0
- package/src/utils/changeCase/noCase.ts +45 -0
- package/src/utils/changeCase/paramCase.ts +11 -0
- package/src/utils/changeCase/pascalCase.ts +24 -0
- package/src/utils/date.ts +1100 -0
- package/src/utils/dom.ts +24 -0
- package/src/utils/getCurrentInstance.ts +43 -0
- package/src/utils/getScrollParent.ts +29 -0
- package/src/utils/globals.ts +8 -0
- package/src/utils/helpers.ts +311 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/propsFactory.ts +92 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@central-design-system/components",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.4",
|
|
4
4
|
"description": "Central Design System on the Vue 3.0 and TypeScript",
|
|
5
5
|
"author": "magersoft",
|
|
6
6
|
"license": "ISC",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
|
+
"src/utils",
|
|
22
23
|
"src/scss",
|
|
23
24
|
"nuxt.js"
|
|
24
25
|
],
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"publish:alpha": "npm publish --tag next"
|
|
31
32
|
},
|
|
32
33
|
"peerDependencies": {
|
|
33
|
-
"vue": "3.2.
|
|
34
|
+
"vue": "^3.2.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/node": "16.18.3",
|
package/src/scss/themes/index.ts
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { pick, isObject, mergeDeep, getNestedValue, getObjectValueByPath, getPropertyFromItem } from '../helpers';
|
|
2
|
+
import {
|
|
3
|
+
mockHelpersIsObject,
|
|
4
|
+
mockHelpersMergeDeep,
|
|
5
|
+
mockHelpersGetNestedValue,
|
|
6
|
+
mockHelpersGetObjectValueByPath,
|
|
7
|
+
mockHelpersGetPropertyFromItem,
|
|
8
|
+
mockHelpersPick
|
|
9
|
+
} from './mocks/helper.mock';
|
|
10
|
+
|
|
11
|
+
describe('isObject', () => {
|
|
12
|
+
mockHelpersIsObject.forEach(({ name, initial, expected }) => {
|
|
13
|
+
it(name, () => {
|
|
14
|
+
expect(isObject(initial)).toEqual(expected);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('mergeDeep', () => {
|
|
20
|
+
mockHelpersMergeDeep.forEach(({ name, initial, expected }) => {
|
|
21
|
+
it(name, () => {
|
|
22
|
+
expect(mergeDeep(initial.source, initial.target, initial.arrayFn)).toEqual(expected);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('getNestedValue', () => {
|
|
28
|
+
mockHelpersGetNestedValue.forEach(({ name, initial, expected }) => {
|
|
29
|
+
it(name, () => {
|
|
30
|
+
expect(getNestedValue(initial.obj, initial.path, initial.fallback)).toEqual(expected);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('getObjectValueByPath', () => {
|
|
36
|
+
mockHelpersGetObjectValueByPath.forEach(({ name, initial, expected }) => {
|
|
37
|
+
it(name, () => {
|
|
38
|
+
expect(getObjectValueByPath(initial.obj, initial.path, initial.fallback)).toEqual(expected);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('getPropertyFromItem', () => {
|
|
44
|
+
mockHelpersGetPropertyFromItem.forEach(({ name, initial, expected }) => {
|
|
45
|
+
it(name, () => {
|
|
46
|
+
expect(getPropertyFromItem(initial.item, initial.property, initial.fallback)).toEqual(expected);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('pick', () => {
|
|
52
|
+
mockHelpersPick.forEach(({ name, initial, expected }) => {
|
|
53
|
+
it(name, () => {
|
|
54
|
+
const [yes, no] = pick(initial.obj, initial.paths);
|
|
55
|
+
expect(yes).toEqual(expected.yes);
|
|
56
|
+
expect(no).toEqual(expected.no);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import type { IMockDataProvider } from '@/types/vitest';
|
|
2
|
+
import type { SelectItemKey } from '@components/utils';
|
|
3
|
+
|
|
4
|
+
export const mockHelpersIsObject: IMockDataProvider[] = [
|
|
5
|
+
{
|
|
6
|
+
name: 'returns true for objects',
|
|
7
|
+
initial: {},
|
|
8
|
+
expected: true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: 'returns true for objects with value',
|
|
12
|
+
initial: { a: 'foo' },
|
|
13
|
+
expected: true
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'returns false for null',
|
|
17
|
+
initial: null,
|
|
18
|
+
expected: false
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'returns false for undefined',
|
|
22
|
+
initial: undefined,
|
|
23
|
+
expected: false
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'returns false for empty array',
|
|
27
|
+
initial: [],
|
|
28
|
+
expected: false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'returns false for string',
|
|
32
|
+
initial: 'str',
|
|
33
|
+
expected: false
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'returns false for number',
|
|
37
|
+
initial: 42,
|
|
38
|
+
expected: false
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'returns false for boolean',
|
|
42
|
+
initial: true,
|
|
43
|
+
expected: false
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'returns false for function',
|
|
47
|
+
initial: () => {},
|
|
48
|
+
expected: false
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
type TMockHelpersMergeDeepInitial = {
|
|
53
|
+
source?: Record<string, any>;
|
|
54
|
+
target?: Record<string, any>;
|
|
55
|
+
arrayFn?: (a: unknown[], b: unknown[]) => unknown[];
|
|
56
|
+
};
|
|
57
|
+
export const mockHelpersMergeDeep: IMockDataProvider<TMockHelpersMergeDeepInitial>[] = [
|
|
58
|
+
{
|
|
59
|
+
name: 'should be merges empty objects',
|
|
60
|
+
initial: {},
|
|
61
|
+
expected: {}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'should be merges simple objects',
|
|
65
|
+
initial: {
|
|
66
|
+
source: { a: 'foo' },
|
|
67
|
+
target: { b: 'bar' }
|
|
68
|
+
},
|
|
69
|
+
expected: {
|
|
70
|
+
a: 'foo',
|
|
71
|
+
b: 'bar'
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'should be deeply merges nested objects',
|
|
76
|
+
initial: {
|
|
77
|
+
source: {
|
|
78
|
+
a: {
|
|
79
|
+
b: {
|
|
80
|
+
c: 'foo'
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
target: {
|
|
85
|
+
a: {
|
|
86
|
+
b: {
|
|
87
|
+
c: 'foo',
|
|
88
|
+
d: 'bar'
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
expected: {
|
|
94
|
+
a: {
|
|
95
|
+
b: {
|
|
96
|
+
c: 'foo',
|
|
97
|
+
d: 'bar'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'should be uses custom arrayFn to merge arrays',
|
|
104
|
+
initial: {
|
|
105
|
+
source: {
|
|
106
|
+
a: [
|
|
107
|
+
{
|
|
108
|
+
a: 'foo'
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
target: {
|
|
113
|
+
a: [
|
|
114
|
+
{
|
|
115
|
+
b: 'bar'
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
arrayFn: (a, b) => a.concat(b)
|
|
120
|
+
},
|
|
121
|
+
expected: {
|
|
122
|
+
a: [{ a: 'foo' }, { b: 'bar' }]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
type TMockHelpersGetNestedValueInitial = {
|
|
128
|
+
obj?: Record<string, any>;
|
|
129
|
+
path: (string | number)[];
|
|
130
|
+
fallback?: any;
|
|
131
|
+
};
|
|
132
|
+
export const mockHelpersGetNestedValue: IMockDataProvider<TMockHelpersGetNestedValueInitial>[] = [
|
|
133
|
+
{
|
|
134
|
+
name: 'should return nested value when path is valid',
|
|
135
|
+
initial: {
|
|
136
|
+
obj: { a: { b: { c: 'x' } } },
|
|
137
|
+
path: ['a', 'b', 'c']
|
|
138
|
+
},
|
|
139
|
+
expected: 'x'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'should return fallback when path is invalid',
|
|
143
|
+
initial: {
|
|
144
|
+
obj: { a: 'x' },
|
|
145
|
+
path: ['a', 'b', 'd'],
|
|
146
|
+
fallback: 'y'
|
|
147
|
+
},
|
|
148
|
+
expected: 'y'
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: 'should return fallback when obj is undefined',
|
|
152
|
+
initial: {
|
|
153
|
+
obj: undefined,
|
|
154
|
+
path: ['a', 'b', 'd'],
|
|
155
|
+
fallback: 'y'
|
|
156
|
+
},
|
|
157
|
+
expected: 'y'
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'should return obj when path is empty array',
|
|
161
|
+
initial: {
|
|
162
|
+
obj: {},
|
|
163
|
+
path: []
|
|
164
|
+
},
|
|
165
|
+
expected: {}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'should return obj with fallback if provided and path is empty array',
|
|
169
|
+
initial: {
|
|
170
|
+
obj: undefined,
|
|
171
|
+
path: [],
|
|
172
|
+
fallback: 'y'
|
|
173
|
+
},
|
|
174
|
+
expected: 'y'
|
|
175
|
+
}
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
type TMockHelpersGetObjectValueByPathInitial = {
|
|
179
|
+
obj?: Record<string, any> | any;
|
|
180
|
+
path: string;
|
|
181
|
+
fallback?: any;
|
|
182
|
+
};
|
|
183
|
+
export const mockHelpersGetObjectValueByPath: IMockDataProvider<TMockHelpersGetObjectValueByPathInitial>[] = [
|
|
184
|
+
{
|
|
185
|
+
name: 'should return nested value when path is valid',
|
|
186
|
+
initial: {
|
|
187
|
+
obj: { a: { b: { c: 'x' } } },
|
|
188
|
+
path: 'a.b.c'
|
|
189
|
+
},
|
|
190
|
+
expected: 'x'
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'should handles indexed arrays',
|
|
194
|
+
initial: {
|
|
195
|
+
obj: { users: [{ name: 'Jane' }, { name: 'John' }] },
|
|
196
|
+
path: 'users[1].name'
|
|
197
|
+
},
|
|
198
|
+
expected: 'John'
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'should return fallback when path is invalid',
|
|
202
|
+
initial: {
|
|
203
|
+
obj: { a: 'x' },
|
|
204
|
+
path: 'a.b.d'
|
|
205
|
+
},
|
|
206
|
+
expected: undefined
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'should return fallback when obj is undefined',
|
|
210
|
+
initial: {
|
|
211
|
+
obj: undefined,
|
|
212
|
+
path: 'a.b.d',
|
|
213
|
+
fallback: 'y'
|
|
214
|
+
},
|
|
215
|
+
expected: 'y'
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: 'should return undefined when path is empty string',
|
|
219
|
+
initial: {
|
|
220
|
+
obj: {},
|
|
221
|
+
path: ''
|
|
222
|
+
},
|
|
223
|
+
expected: undefined
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'should return obj with fallback if provided and path is empty string',
|
|
227
|
+
initial: {
|
|
228
|
+
obj: undefined,
|
|
229
|
+
path: '',
|
|
230
|
+
fallback: 'y'
|
|
231
|
+
},
|
|
232
|
+
expected: 'y'
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "should return obj[path] if it isn't undefined",
|
|
236
|
+
initial: {
|
|
237
|
+
obj: { firstKey: 'value' },
|
|
238
|
+
path: 'firstKey',
|
|
239
|
+
fallback: 'fallbackValue'
|
|
240
|
+
},
|
|
241
|
+
expected: 'value'
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'should return fallback in case of invalid input data',
|
|
245
|
+
initial: {
|
|
246
|
+
obj: null,
|
|
247
|
+
path: null as any,
|
|
248
|
+
fallback: 5
|
|
249
|
+
},
|
|
250
|
+
expected: 5
|
|
251
|
+
}
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
type TMockHelpersGetPropertyFromItem = {
|
|
255
|
+
item: any;
|
|
256
|
+
property: SelectItemKey;
|
|
257
|
+
fallback?: any;
|
|
258
|
+
};
|
|
259
|
+
export const mockHelpersGetPropertyFromItem: IMockDataProvider<TMockHelpersGetPropertyFromItem>[] = [
|
|
260
|
+
{
|
|
261
|
+
name: 'should return the item if property is null',
|
|
262
|
+
initial: {
|
|
263
|
+
item: 'item',
|
|
264
|
+
property: null as any,
|
|
265
|
+
fallback: 'fallback'
|
|
266
|
+
},
|
|
267
|
+
expected: 'item'
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'should return the item if property is undefined',
|
|
271
|
+
initial: {
|
|
272
|
+
item: undefined,
|
|
273
|
+
property: null as any,
|
|
274
|
+
fallback: 'fallback'
|
|
275
|
+
},
|
|
276
|
+
expected: 'fallback'
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'should return the fallback if item is not an object',
|
|
280
|
+
initial: {
|
|
281
|
+
item: 1,
|
|
282
|
+
property: 'path',
|
|
283
|
+
fallback: 'fallback'
|
|
284
|
+
},
|
|
285
|
+
expected: 'fallback'
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: 'should return the correct value for a string property',
|
|
289
|
+
initial: {
|
|
290
|
+
item: { path: 'value' },
|
|
291
|
+
property: 'path'
|
|
292
|
+
},
|
|
293
|
+
expected: 'value'
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'should return the correct value for an array property and fallback when not found',
|
|
297
|
+
initial: {
|
|
298
|
+
item: { path: { subpath: 'value' } },
|
|
299
|
+
property: ['path', 'subpath'],
|
|
300
|
+
fallback: 'fallback'
|
|
301
|
+
},
|
|
302
|
+
expected: 'value'
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'should return the correct value for a function property',
|
|
306
|
+
initial: {
|
|
307
|
+
item: { path: 'value' },
|
|
308
|
+
property: (item: any) => item.path,
|
|
309
|
+
fallback: 'fallback'
|
|
310
|
+
},
|
|
311
|
+
expected: 'value'
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: 'should return the fallback if the function returns undefined',
|
|
315
|
+
initial: {
|
|
316
|
+
item: { path: 'value' },
|
|
317
|
+
property: () => undefined,
|
|
318
|
+
fallback: 'fallback'
|
|
319
|
+
},
|
|
320
|
+
expected: 'fallback'
|
|
321
|
+
}
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
type TMockHelpersPick = {
|
|
325
|
+
obj: Record<string, any>;
|
|
326
|
+
paths: string[] | RegExp[] | any[];
|
|
327
|
+
};
|
|
328
|
+
export const mockHelpersPick: IMockDataProvider<TMockHelpersPick>[] = [
|
|
329
|
+
{
|
|
330
|
+
name: 'should correctly selects specified properties and returns both picked and unpicked objects',
|
|
331
|
+
initial: {
|
|
332
|
+
obj: { a: 1, b: 2, c: 3 },
|
|
333
|
+
paths: ['a', 'c']
|
|
334
|
+
},
|
|
335
|
+
expected: {
|
|
336
|
+
yes: { a: 1, c: 3 },
|
|
337
|
+
no: { b: 2 }
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: 'should correctly selects specified properties using regex',
|
|
342
|
+
initial: {
|
|
343
|
+
obj: { a: 1, b: 2, c: 3 },
|
|
344
|
+
paths: [/^[ab]$/]
|
|
345
|
+
},
|
|
346
|
+
expected: {
|
|
347
|
+
yes: { a: 1, b: 2 },
|
|
348
|
+
no: { c: 3 }
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: 'should returns empty objects for non-existing properties',
|
|
353
|
+
initial: {
|
|
354
|
+
obj: { a: 1, b: 2, c: 3 },
|
|
355
|
+
paths: ['x', 'y', 'z']
|
|
356
|
+
},
|
|
357
|
+
expected: {
|
|
358
|
+
yes: {},
|
|
359
|
+
no: { a: 1, b: 2, c: 3 }
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { IMockDataProvider } from '@/types/vitest';
|
|
2
|
+
import { ComponentObjectPropsOptions } from '@components/shims-vue';
|
|
3
|
+
|
|
4
|
+
export const mockPropFactory: IMockDataProvider<
|
|
5
|
+
{ props: ComponentObjectPropsOptions; source: string; defaults?: any },
|
|
6
|
+
ComponentObjectPropsOptions
|
|
7
|
+
>[] = [
|
|
8
|
+
{
|
|
9
|
+
name: 'propFactory',
|
|
10
|
+
initial: {
|
|
11
|
+
props: {
|
|
12
|
+
foo: String,
|
|
13
|
+
bar: Array
|
|
14
|
+
},
|
|
15
|
+
source: 'props'
|
|
16
|
+
},
|
|
17
|
+
expected: {
|
|
18
|
+
foo: {
|
|
19
|
+
source: 'props',
|
|
20
|
+
type: String
|
|
21
|
+
},
|
|
22
|
+
bar: {
|
|
23
|
+
source: 'props',
|
|
24
|
+
type: Array
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'propFactory',
|
|
30
|
+
initial: {
|
|
31
|
+
props: {
|
|
32
|
+
test: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: ''
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
source: 'test'
|
|
38
|
+
},
|
|
39
|
+
expected: {
|
|
40
|
+
test: {
|
|
41
|
+
source: 'test',
|
|
42
|
+
type: String,
|
|
43
|
+
default: ''
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'propFactory',
|
|
49
|
+
initial: {
|
|
50
|
+
props: {
|
|
51
|
+
bar: {
|
|
52
|
+
type: Object,
|
|
53
|
+
default: {}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
source: 'bar'
|
|
57
|
+
},
|
|
58
|
+
expected: {
|
|
59
|
+
bar: {
|
|
60
|
+
source: 'bar',
|
|
61
|
+
type: Object,
|
|
62
|
+
default: {}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'propFactory',
|
|
68
|
+
initial: {
|
|
69
|
+
props: {
|
|
70
|
+
foo: {
|
|
71
|
+
type: String,
|
|
72
|
+
default: undefined
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
source: 'foo',
|
|
76
|
+
defaults: {
|
|
77
|
+
foo: 'string'
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
expected: {
|
|
81
|
+
foo: {
|
|
82
|
+
default: 'string',
|
|
83
|
+
source: 'foo',
|
|
84
|
+
type: String
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { propsFactory } from '../propsFactory';
|
|
2
|
+
import { mockPropFactory } from './mocks/propFactory.mock';
|
|
3
|
+
|
|
4
|
+
describe('propFactory.ts', () => {
|
|
5
|
+
it.each(mockPropFactory)('should be props', ({ initial, expected }) => {
|
|
6
|
+
const makeProps = propsFactory(initial.props, initial.source);
|
|
7
|
+
expect(makeProps(initial.defaults)).toEqual(expected);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { includes } from './';
|
|
2
|
+
|
|
3
|
+
const block = ['top', 'bottom'] as const;
|
|
4
|
+
const inline = ['start', 'end', 'left', 'right'] as const;
|
|
5
|
+
type Tblock = typeof block[number];
|
|
6
|
+
type Tinline = typeof inline[number];
|
|
7
|
+
export type TAnchor =
|
|
8
|
+
| Tblock
|
|
9
|
+
| Tinline
|
|
10
|
+
| 'center'
|
|
11
|
+
| 'center center'
|
|
12
|
+
| `${Tblock} ${Tinline | 'center'}`
|
|
13
|
+
| `${Tinline} ${Tblock | 'center'}`;
|
|
14
|
+
export type ParsedAnchor =
|
|
15
|
+
| { side: 'center'; align: 'center' }
|
|
16
|
+
| { side: Tblock; align: 'left' | 'right' | 'center' }
|
|
17
|
+
| { side: 'left' | 'right'; align: Tblock | 'center' };
|
|
18
|
+
|
|
19
|
+
/** Parse a raw anchor string into an object */
|
|
20
|
+
export function parseAnchor(anchor: TAnchor) {
|
|
21
|
+
const anchorArr = anchor.split(' ') as [Tblock | Tinline | 'center', Tblock | Tinline | 'center' | undefined];
|
|
22
|
+
const side = anchorArr[0];
|
|
23
|
+
let align = anchorArr[1];
|
|
24
|
+
|
|
25
|
+
if (!align) {
|
|
26
|
+
align = includes(block, side) ? 'start' : includes(inline, side) ? 'top' : 'center';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
side: toPhysical(side),
|
|
31
|
+
align: toPhysical(align)
|
|
32
|
+
} as ParsedAnchor;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function toPhysical(str: 'center' | Tblock | Tinline) {
|
|
36
|
+
if (str === 'start') return 'left';
|
|
37
|
+
if (str === 'end') return 'right';
|
|
38
|
+
return str;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function flipSide(anchor: ParsedAnchor) {
|
|
42
|
+
return {
|
|
43
|
+
side: {
|
|
44
|
+
center: 'center',
|
|
45
|
+
top: 'bottom',
|
|
46
|
+
bottom: 'top',
|
|
47
|
+
left: 'right',
|
|
48
|
+
right: 'left'
|
|
49
|
+
}[anchor.side],
|
|
50
|
+
align: anchor.align
|
|
51
|
+
} as ParsedAnchor;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function flipAlign(anchor: ParsedAnchor) {
|
|
55
|
+
return {
|
|
56
|
+
side: anchor.side,
|
|
57
|
+
align: {
|
|
58
|
+
center: 'center',
|
|
59
|
+
top: 'bottom',
|
|
60
|
+
bottom: 'top',
|
|
61
|
+
left: 'right',
|
|
62
|
+
right: 'left'
|
|
63
|
+
}[anchor.align]
|
|
64
|
+
} as ParsedAnchor;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function flipCorner(anchor: ParsedAnchor) {
|
|
68
|
+
return {
|
|
69
|
+
side: anchor.align,
|
|
70
|
+
align: anchor.side
|
|
71
|
+
} as ParsedAnchor;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getAxis(anchor: ParsedAnchor) {
|
|
75
|
+
return includes(block, anchor.side) ? 'y' : 'x';
|
|
76
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Box } from './box';
|
|
2
|
+
|
|
3
|
+
/** @see https://stackoverflow.com/a/57876601/2074736 */
|
|
4
|
+
export function nullifyTransforms(el: HTMLElement): Box {
|
|
5
|
+
const rect = el.getBoundingClientRect();
|
|
6
|
+
const style = getComputedStyle(el);
|
|
7
|
+
const tx = style.transform;
|
|
8
|
+
|
|
9
|
+
if (tx) {
|
|
10
|
+
let ta, sx, sy, dx, dy;
|
|
11
|
+
if (tx.startsWith('matrix3d(')) {
|
|
12
|
+
ta = tx.slice(9, -1).split(/, /);
|
|
13
|
+
sx = +ta[0];
|
|
14
|
+
sy = +ta[5];
|
|
15
|
+
dx = +ta[12];
|
|
16
|
+
dy = +ta[13];
|
|
17
|
+
} else if (tx.startsWith('matrix(')) {
|
|
18
|
+
ta = tx.slice(7, -1).split(/, /);
|
|
19
|
+
sx = +ta[0];
|
|
20
|
+
sy = +ta[3];
|
|
21
|
+
dx = +ta[4];
|
|
22
|
+
dy = +ta[5];
|
|
23
|
+
} else {
|
|
24
|
+
return new Box(rect);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const to = style.transformOrigin;
|
|
28
|
+
const x = rect.x - dx - (1 - sx) * parseFloat(to);
|
|
29
|
+
const y = rect.y - dy - (1 - sy) * parseFloat(to.slice(to.indexOf(' ') + 1));
|
|
30
|
+
const w = sx ? rect.width / sx : el.offsetWidth + 1;
|
|
31
|
+
const h = sy ? rect.height / sy : el.offsetHeight + 1;
|
|
32
|
+
|
|
33
|
+
return new Box({ x, y, width: w, height: h });
|
|
34
|
+
} else {
|
|
35
|
+
return new Box(rect);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function animate(
|
|
40
|
+
el: Element,
|
|
41
|
+
keyframes: Keyframe[] | PropertyIndexedKeyframes | null,
|
|
42
|
+
options?: number | KeyframeAnimationOptions
|
|
43
|
+
) {
|
|
44
|
+
if (typeof el.animate === 'undefined') return { finished: Promise.resolve() };
|
|
45
|
+
|
|
46
|
+
let animation: Animation;
|
|
47
|
+
try {
|
|
48
|
+
animation = el.animate(keyframes, options);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return { finished: Promise.resolve() };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof animation.finished === 'undefined') {
|
|
54
|
+
(animation as any).finished = new Promise((resolve) => {
|
|
55
|
+
animation.onfinish = () => {
|
|
56
|
+
resolve(animation);
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return animation;
|
|
62
|
+
}
|