neo.mjs 6.3.11 → 6.4.1
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/apps/ServiceWorker.mjs +2 -2
- package/examples/ConfigurationViewport.mjs +21 -9
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/container/dialog/MainContainerController.mjs +20 -16
- package/examples/dialog/DemoDialog.mjs +24 -3
- package/examples/form/field/select/MainContainer.mjs +28 -6
- package/package.json +1 -1
- package/resources/scss/src/component/Base.scss +26 -0
- package/resources/scss/src/form/field/Picker.scss +0 -3
- package/resources/scss/src/menu/List.scss +4 -8
- package/resources/scss/theme-dark/menu/List.scss +2 -1
- package/resources/scss/theme-light/menu/List.scss +1 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/button/Base.mjs +41 -6
- package/src/component/Base.mjs +236 -44
- package/src/container/Dialog.mjs +3 -3
- package/src/core/Base.mjs +20 -0
- package/src/form/Container.mjs +2 -0
- package/src/form/field/Picker.mjs +30 -47
- package/src/form/field/Time.mjs +8 -8
- package/src/form/field/trigger/Base.mjs +1 -1
- package/src/form/field/trigger/CopyToClipboard.mjs +5 -1
- package/src/grid/View.mjs +5 -2
- package/src/grid/header/Button.mjs +10 -10
- package/src/list/Base.mjs +17 -14
- package/src/list/plugin/Animate.mjs +3 -3
- package/src/main/DomAccess.mjs +272 -28
- package/src/menu/List.mjs +35 -90
- package/src/table/Container.mjs +2 -2
- package/src/table/header/Button.mjs +21 -23
- package/src/tree/Accordion.mjs +1 -1
- package/src/util/Array.mjs +4 -18
- package/src/util/Css.mjs +6 -8
- package/src/util/HashHistory.mjs +10 -3
- package/src/util/Rectangle.mjs +444 -7
- package/test/siesta/siesta-node.js +2 -1
- package/test/siesta/siesta.js +1 -0
- package/test/siesta/tests/Rectangle.mjs +409 -0
package/src/util/Array.mjs
CHANGED
@@ -132,29 +132,15 @@ class NeoArray extends Base {
|
|
132
132
|
}
|
133
133
|
|
134
134
|
/**
|
135
|
-
* Returns an array of items which are present in
|
135
|
+
* Returns an array of items which are present in the passed arrays.
|
136
|
+
* Multiple arrays may be passed.
|
136
137
|
* Only supports primitive items
|
137
138
|
* @param {Array} array1
|
138
139
|
* @param {Array} array2
|
139
140
|
* @returns {Array}
|
140
141
|
*/
|
141
|
-
static union(
|
142
|
-
|
143
|
-
merge = array1.concat(array2),
|
144
|
-
len = merge.length,
|
145
|
-
assoc = {},
|
146
|
-
item;
|
147
|
-
|
148
|
-
while (len--) {
|
149
|
-
item = merge[len];
|
150
|
-
|
151
|
-
if (!assoc[item]) {
|
152
|
-
result.unshift(item);
|
153
|
-
assoc[item] = true;
|
154
|
-
}
|
155
|
-
}
|
156
|
-
|
157
|
-
return result;
|
142
|
+
static union() {
|
143
|
+
return [...new Set(Array.prototype.concat(...arguments))];
|
158
144
|
}
|
159
145
|
|
160
146
|
/**
|
package/src/util/Css.mjs
CHANGED
@@ -15,29 +15,27 @@ class Css extends Base {
|
|
15
15
|
|
16
16
|
/**
|
17
17
|
* Pass the selectorText of the rules which you want to remove
|
18
|
+
* @param {String} appName
|
18
19
|
* @param {String[]|String} rules
|
19
20
|
*/
|
20
|
-
static deleteRules(rules) {
|
21
|
+
static deleteRules(appName, rules) {
|
21
22
|
if (!Array.isArray(rules)) {
|
22
23
|
rules = [rules];
|
23
24
|
}
|
24
25
|
|
25
|
-
Neo.main.addon.Stylesheet.deleteCssRules({
|
26
|
-
rules: rules
|
27
|
-
});
|
26
|
+
Neo.main.addon.Stylesheet.deleteCssRules({appName, rules})
|
28
27
|
}
|
29
28
|
|
30
29
|
/**
|
30
|
+
* @param {String} appName
|
31
31
|
* @param {String[]|String} rules
|
32
32
|
*/
|
33
|
-
static insertRules(rules) {
|
33
|
+
static insertRules(appName, rules) {
|
34
34
|
if (!Array.isArray(rules)) {
|
35
35
|
rules = [rules];
|
36
36
|
}
|
37
37
|
|
38
|
-
Neo.main.addon.Stylesheet.insertCssRules({
|
39
|
-
rules: rules
|
40
|
-
});
|
38
|
+
Neo.main.addon.Stylesheet.insertCssRules({appName, rules})
|
41
39
|
}
|
42
40
|
}
|
43
41
|
|
package/src/util/HashHistory.mjs
CHANGED
@@ -41,14 +41,14 @@ class HashHistory extends Base {
|
|
41
41
|
* @returns {Object}
|
42
42
|
*/
|
43
43
|
first() {
|
44
|
-
return this.stack[0]
|
44
|
+
return this.stack[0] || null
|
45
45
|
}
|
46
46
|
|
47
47
|
/**
|
48
48
|
* @returns {Number}
|
49
49
|
*/
|
50
50
|
getCount() {
|
51
|
-
return this.stack.length
|
51
|
+
return this.stack.length
|
52
52
|
}
|
53
53
|
|
54
54
|
/**
|
@@ -65,12 +65,19 @@ class HashHistory extends Base {
|
|
65
65
|
stack.unshift(data);
|
66
66
|
|
67
67
|
if (stack.length > me.maxItems) {
|
68
|
-
stack.pop()
|
68
|
+
stack.pop()
|
69
69
|
}
|
70
70
|
|
71
71
|
me.fire('change', data, stack[1] || null)
|
72
72
|
}
|
73
73
|
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* @returns {Object}
|
77
|
+
*/
|
78
|
+
second() {
|
79
|
+
return this.stack[1] || null
|
80
|
+
}
|
74
81
|
}
|
75
82
|
|
76
83
|
let instance = Neo.applyClassConfig(HashHistory);
|
package/src/util/Rectangle.mjs
CHANGED
@@ -1,11 +1,84 @@
|
|
1
|
-
import Base from '../core/Base.mjs';
|
2
|
-
|
3
1
|
/**
|
4
2
|
* The class contains utility methods for working with DOMRect Objects
|
5
3
|
* @class Neo.util.Rectangle
|
6
|
-
* @extends
|
4
|
+
* @extends DOMRect
|
7
5
|
*/
|
8
|
-
|
6
|
+
|
7
|
+
const
|
8
|
+
emptyArray = Object.freeze([]),
|
9
|
+
// Convert edge array values into the [T,R,B,L] form.
|
10
|
+
parseEdgeValue = (e = 0) => {
|
11
|
+
if (!Array.isArray(e)) {
|
12
|
+
e = [e];
|
13
|
+
}
|
14
|
+
switch (e.length) {
|
15
|
+
case 1:
|
16
|
+
e.length = 4;
|
17
|
+
return e.fill(e[0], 1, 4);
|
18
|
+
case 2:// top&bottom, left&right
|
19
|
+
return [e[0], e[1], e[0], e[1]];
|
20
|
+
case 3:// top, left&right, bottom
|
21
|
+
return [e[0], e[1], e[2], e[1]];
|
22
|
+
}
|
23
|
+
return e;
|
24
|
+
},
|
25
|
+
parseEdgeAlign = edgeAlign => {
|
26
|
+
const
|
27
|
+
edgeParts = edgeAlignRE.exec(edgeAlign),
|
28
|
+
ourEdgeZone = edgeZone[edgeParts[1]],
|
29
|
+
theirEdgeZone = edgeZone[edgeParts[4]];
|
30
|
+
|
31
|
+
return {
|
32
|
+
ourEdge : edgeParts[1],
|
33
|
+
ourEdgeOffset : parseInt(edgeParts[2] || 50),
|
34
|
+
ourEdgeUnit : edgeParts[3] || '%',
|
35
|
+
ourEdgeZone,
|
36
|
+
theirEdge : edgeParts[4],
|
37
|
+
theirEdgeOffset : parseInt(edgeParts[5] || 50),
|
38
|
+
theirEdgeUnit : edgeParts[6] || '%',
|
39
|
+
theirEdgeZone,
|
40
|
+
|
41
|
+
// Aligned to an edge, *outside* of the target.
|
42
|
+
// A normal align as a combo dropdown might request
|
43
|
+
edgeAligned : (ourEdgeZone & 1) === (theirEdgeZone & 1) && ourEdgeZone !== theirEdgeZone
|
44
|
+
}
|
45
|
+
},
|
46
|
+
// The opposite of parseEdgeAlign, and it has to flip the edges
|
47
|
+
createReversedEdgeAlign = edges => {
|
48
|
+
const
|
49
|
+
ourEdge = oppositeEdge[edges.ourEdge],
|
50
|
+
theirEdge = oppositeEdge[edges.theirEdge];
|
51
|
+
|
52
|
+
// reconstitute a rule string with the edges flipped to the opposite sides
|
53
|
+
return `${ourEdge}${edges.ourEdgeOffset}${edges.ourEdgeUnit}-${theirEdge}${edges.theirEdgeOffset}${edges.theirEdgeUnit}`
|
54
|
+
|
55
|
+
},
|
56
|
+
getElRect = el => {
|
57
|
+
const r = el instanceof DOMRect ? el : (el?.nodeType === 1 ? el : typeof el === 'string' ? document.getElementById(el) : null)?.getBoundingClientRect();
|
58
|
+
|
59
|
+
// Convert DOMRect into Rectangle
|
60
|
+
return r && new Rectangle(r.x, r.y, r.width, r.height);
|
61
|
+
},
|
62
|
+
oppositeEdge = {
|
63
|
+
t : 'b',
|
64
|
+
r : 'l',
|
65
|
+
b : 't',
|
66
|
+
l : 'r'
|
67
|
+
},
|
68
|
+
edgeZone = {
|
69
|
+
t : 0,
|
70
|
+
r : 1,
|
71
|
+
b : 2,
|
72
|
+
l : 3
|
73
|
+
},
|
74
|
+
zoneNames = ['top', 'right', 'bottom', 'left'],
|
75
|
+
zoneEdges = ['t', 'r', 'b', 'l'],
|
76
|
+
zoneDimension = ['width', 'height'],
|
77
|
+
zoneCoord = [0, 1, 0, 1],
|
78
|
+
zeroMargins = [0, 0, 0, 0],
|
79
|
+
edgeAlignRE = /^([trblc])(\d*)(%|px)?-([trblc])(\d*)(%|px)?$/;
|
80
|
+
|
81
|
+
export default class Rectangle extends DOMRect {
|
9
82
|
static config = {
|
10
83
|
/**
|
11
84
|
* @member {String} className='Neo.util.Rectangle'
|
@@ -147,8 +220,372 @@ class Rectangle extends Base {
|
|
147
220
|
|
148
221
|
return movedRect;
|
149
222
|
}
|
150
|
-
}
|
151
223
|
|
152
|
-
|
224
|
+
set bottom(b) {
|
225
|
+
this.height += b - this.bottom;
|
226
|
+
}
|
227
|
+
get bottom() {
|
228
|
+
return super.bottom;
|
229
|
+
}
|
230
|
+
|
231
|
+
set right(r) {
|
232
|
+
this.width += r - this.right;
|
233
|
+
}
|
234
|
+
get right() {
|
235
|
+
return super.right;
|
236
|
+
}
|
237
|
+
|
238
|
+
// Change the x without moving the Rectangle. The left side moves and the right side doesn't
|
239
|
+
changeX(x) {
|
240
|
+
const widthDelta = this.x - x;
|
241
|
+
|
242
|
+
this.x = x;
|
243
|
+
this.width += widthDelta;
|
244
|
+
}
|
245
|
+
|
246
|
+
// Change the y without moving the Rectangle. The top side moves and the bottom side doesn't
|
247
|
+
changeY(y) {
|
248
|
+
const heightDelta = this.y - y;
|
249
|
+
|
250
|
+
this.y = y;
|
251
|
+
this.height += heightDelta;
|
252
|
+
}
|
253
|
+
|
254
|
+
clone() {
|
255
|
+
return Rectangle.clone(this);
|
256
|
+
}
|
257
|
+
|
258
|
+
static clone(r) {
|
259
|
+
const result = new Rectangle(r.x, r.y, r.width, r.height);
|
260
|
+
|
261
|
+
result.minWidth = r.minWidth;
|
262
|
+
result.minHeight = r.minHeight;
|
263
|
+
|
264
|
+
return result;
|
265
|
+
}
|
266
|
+
|
267
|
+
intersects(other) {
|
268
|
+
const me = this;
|
269
|
+
|
270
|
+
if (other.height && other.width) {
|
271
|
+
const
|
272
|
+
left = Math.max(me.x, other.x),
|
273
|
+
top = Math.max(me.y, other.y),
|
274
|
+
right = Math.min(me.x + me.width, other.x + other.width),
|
275
|
+
bottom = Math.min(me.y + me.height, other.y + other.height);
|
276
|
+
|
277
|
+
if (left >= right || top >= bottom) {
|
278
|
+
return false;
|
279
|
+
}
|
280
|
+
|
281
|
+
return new Rectangle(left, top, right - left, bottom - top);
|
282
|
+
}
|
283
|
+
// We're dealing with a point here - zero dimensions
|
284
|
+
else {
|
285
|
+
return (other.x >= me.x && other.y >= me.y && other.right <= me.right && other.bottom <= me.bottom);
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
/**
|
290
|
+
* Checks if the other Rectangle is fully contained inside this Rectangle
|
291
|
+
* @param {Object} other
|
292
|
+
* @returns {Boolean}
|
293
|
+
*/
|
294
|
+
contains(other) {
|
295
|
+
return this.bottom >= other.bottom
|
296
|
+
&& this.left <= other.left
|
297
|
+
&& this.right >= other.right
|
298
|
+
&& this.top <= other.top;
|
299
|
+
}
|
300
|
+
|
301
|
+
/**
|
302
|
+
* Returns a clone of this Rectangle expanded according to the edges array.
|
303
|
+
* @param {Number}Number[]} edges
|
304
|
+
* @returns
|
305
|
+
*/
|
306
|
+
expand(edges) {
|
307
|
+
edges = parseEdgeValue(edges);
|
308
|
+
|
309
|
+
return new this.constructor(this.x - edges[3], this.y - edges[0], this.width + edges[1] + edges[3], this.height + edges[0] + edges[2]);
|
310
|
+
}
|
153
311
|
|
154
|
-
|
312
|
+
moveBy(x = 0, y = 0) {
|
313
|
+
const result = this.clone();
|
314
|
+
|
315
|
+
if (Array.isArray(x)) {
|
316
|
+
y = x[1];
|
317
|
+
x = x[0];
|
318
|
+
}
|
319
|
+
result.x += x;
|
320
|
+
result.y += y;
|
321
|
+
return result;
|
322
|
+
}
|
323
|
+
|
324
|
+
/**
|
325
|
+
* Returns `true` if this Rectangle completely contains the other Rectangle
|
326
|
+
* @param {Rectangle} other
|
327
|
+
*/
|
328
|
+
contains(other) {
|
329
|
+
return this.constructor.includes(this, other);
|
330
|
+
}
|
331
|
+
|
332
|
+
/**
|
333
|
+
* Returns a copy of this Rectangle constrained to fit within the passed Rectangle
|
334
|
+
* @param {Rectangle} constrainTo
|
335
|
+
* @returns {Rectangle|Boolean} A new Rectangle constrained to te passed Rectangle, or false if it could not be constrained.
|
336
|
+
*/
|
337
|
+
constrainTo(constrainTo) {
|
338
|
+
const
|
339
|
+
me = this,
|
340
|
+
minWidth = me.minWidth || me.width,
|
341
|
+
minHeight = me.minHeight || me.height;
|
342
|
+
|
343
|
+
// Not possible, even when shrunk to minima
|
344
|
+
if (minHeight > constrainTo.height || minWidth > constrainTo.width) {
|
345
|
+
return false;
|
346
|
+
}
|
347
|
+
|
348
|
+
// We do not mutate this Rectangle, but return a constrained version
|
349
|
+
const result = me.clone();
|
350
|
+
|
351
|
+
// Translate result so that the top and left are visible
|
352
|
+
result.x = Math.max(me.x + Math.min(constrainTo.right - result.right, 0), constrainTo.x);
|
353
|
+
result.y = Math.max(me.y + Math.min(constrainTo.bottom - result.bottom, 0), constrainTo.y);
|
354
|
+
|
355
|
+
// Pull in any resulting overflow
|
356
|
+
result.bottom = Math.min(result.bottom, constrainTo.bottom);
|
357
|
+
result.right = Math.min(result.right, constrainTo.right);
|
358
|
+
|
359
|
+
return result;
|
360
|
+
}
|
361
|
+
|
362
|
+
alignTo(align) {
|
363
|
+
const
|
364
|
+
me = this,
|
365
|
+
{
|
366
|
+
minWidth,
|
367
|
+
minHeight
|
368
|
+
} = me,
|
369
|
+
{
|
370
|
+
constrainTo, // Element or Rectangle result must fit into
|
371
|
+
target, // Element or Rectangle to align to
|
372
|
+
edgeAlign, // t50-b50 type string
|
373
|
+
axisLock, // true for flip, 'flexible' for flip, then try the other edges
|
374
|
+
offset, // Final [x, y] vector to move the result by.
|
375
|
+
matchSize
|
376
|
+
} = align,
|
377
|
+
targetMargin = align.targetMargin ? parseEdgeValue(align.targetMargin) : zeroMargins,
|
378
|
+
targetRect = getElRect(target),
|
379
|
+
constrainRect = getElRect(constrainTo),
|
380
|
+
edges = parseEdgeAlign(edgeAlign),
|
381
|
+
matchDimension = zoneDimension[edges.theirEdgeZone & 1];
|
382
|
+
|
383
|
+
let result = me.clone();
|
384
|
+
|
385
|
+
if (matchSize) {
|
386
|
+
result[matchDimension] = targetRect[matchDimension];
|
387
|
+
}
|
388
|
+
|
389
|
+
// Must do the calculations after the aligned side has been matched in size if requested.
|
390
|
+
const
|
391
|
+
myPoint = result.getAnchorPoint(edges.ourEdgeZone, edges.ourEdgeOffset, edges.ourEdgeUnit),
|
392
|
+
targetPoint = targetRect.getAnchorPoint(edges.theirEdgeZone, edges.theirEdgeOffset, edges.theirEdgeUnit, targetMargin),
|
393
|
+
vector = [targetPoint[0] - myPoint[0], targetPoint[1] - myPoint[1]];
|
394
|
+
|
395
|
+
result = result.moveBy(vector);
|
396
|
+
|
397
|
+
// A useful property in the resulting rectangle which specifies which zone of the target
|
398
|
+
// It is being places in, T,R,B or L - 0, 1, 2, 3
|
399
|
+
// Some code may want to treat DOM elements differently depending on the zone
|
400
|
+
result.zone = edges.theirEdgeZone;
|
401
|
+
result.position = zoneNames[result.zone];
|
402
|
+
|
403
|
+
// Now we create the four Rectangles around the target, into which we may be constrained
|
404
|
+
// Zones T,R,B,L 0 9, 1, 2, 3:
|
405
|
+
// +-----------------------------------------------------------------------------------+
|
406
|
+
// | +-------------------------+------------------------+----------------------------+ |
|
407
|
+
// | | ^ | | ^ | |
|
408
|
+
// | | | | | | | |
|
409
|
+
// | | <-------+--------------+---------Zone 0---------+-------------+----------> | |
|
410
|
+
// | | | | | | | |
|
411
|
+
// | | | | | | | |
|
412
|
+
// | +----------+--------------+------------------------+-------------+--------------+ |
|
413
|
+
// | | | | +--------------------+ | | | |
|
414
|
+
// | | | | | | | | | |
|
415
|
+
// | | | | | | | | | |
|
416
|
+
// | | Zone 3 | | | | Zone 1 | |
|
417
|
+
// | | | | | | | | | |
|
418
|
+
// | | | | | | | | | |
|
419
|
+
// | | | | +--------------------+ | | | |
|
420
|
+
// | ++---------+--------------+------------------------+-------------+--------------+ |
|
421
|
+
// | | | | | | | |
|
422
|
+
// | | | | | | | |
|
423
|
+
// | | | | | | | |
|
424
|
+
// | | <-------+--------------+--------Zone 2----------+-------------+------------> | |
|
425
|
+
// | | | | | | | |
|
426
|
+
// | | v | | v | |
|
427
|
+
// | ++------------------------+------------------------+----------------------------+ |
|
428
|
+
// +-----------------------------------------------------------------------------------+
|
429
|
+
if (constrainRect && !constrainRect.contains(result)) {
|
430
|
+
// They asked to overlap the target, for example t0-t0
|
431
|
+
// In these cases, we just return the result
|
432
|
+
if (targetRect.intersects(result)) {
|
433
|
+
return result;
|
434
|
+
}
|
435
|
+
|
436
|
+
// This is the zone we try to fit into first, the one that was asked for
|
437
|
+
let zone = edges.theirEdgeZone;
|
438
|
+
|
439
|
+
// We create an array of four rectangles into which we try to fit with appropriate align specs.
|
440
|
+
// We must start with the requested zone, whatever that is.
|
441
|
+
const zonesToTry = [{
|
442
|
+
zone,
|
443
|
+
edgeAlign
|
444
|
+
}];
|
445
|
+
|
446
|
+
if (axisLock) {
|
447
|
+
// Flip to the opposite side for the second try.
|
448
|
+
// The alignment string has to be reversed
|
449
|
+
// so r20-l30 has to become l20-r30.
|
450
|
+
// The other two zones revert to centered so are easier
|
451
|
+
zonesToTry[1] = {
|
452
|
+
zone : zone = (zone + 2) % 4,
|
453
|
+
edgeAlign : createReversedEdgeAlign(edges)
|
454
|
+
}
|
455
|
+
|
456
|
+
// Fall back to the other two zones if we are allowed to
|
457
|
+
if (axisLock === 'flexible') {
|
458
|
+
zonesToTry.push({
|
459
|
+
zone : zone = (alignSpec.startZone + 1) % 4,
|
460
|
+
edgeAlign : `${oppositeEdge[zoneEdges[zone]]}-${zoneEdges[zone]}`
|
461
|
+
});
|
462
|
+
zonesToTry.push({
|
463
|
+
zone : zone = (zone + 2) % 4,
|
464
|
+
edgeAlign : `${oppositeEdge[zoneEdges[zone]]}-${zoneEdges[zone]}`
|
465
|
+
});
|
466
|
+
}
|
467
|
+
}
|
468
|
+
else {
|
469
|
+
// go through the other zones in order
|
470
|
+
for (let i = 1; i < 4; i++) {
|
471
|
+
zonesToTry.push({
|
472
|
+
zone : zone = (zone + 1) % 4,
|
473
|
+
edgeAlign : `${oppositeEdge[zoneEdges[zone]]}-${zoneEdges[zone]}`
|
474
|
+
});
|
475
|
+
}
|
476
|
+
}
|
477
|
+
|
478
|
+
// Calculate the constraint Rectangle for each zone
|
479
|
+
for (let i = 0; i < zonesToTry.length; i++) {
|
480
|
+
// We clone the outer constraining rectangle
|
481
|
+
// and move it into position
|
482
|
+
const c = constrainRect.clone();
|
483
|
+
|
484
|
+
switch (zonesToTry[i].zone) {
|
485
|
+
case 0:
|
486
|
+
// The zone i2 above the target - zone 0/T
|
487
|
+
c.bottom = targetRect.y - targetMargin[0];
|
488
|
+
break;
|
489
|
+
case 1:
|
490
|
+
// The zone is to the right of the target - zone 1/R
|
491
|
+
c.changeX(targetRect.right + targetMargin[1]);
|
492
|
+
break;
|
493
|
+
case 2:
|
494
|
+
// The zone is below the target - zone 2/B
|
495
|
+
c.changeY(targetRect.bottom + targetMargin[2]);
|
496
|
+
break;
|
497
|
+
case 3:
|
498
|
+
// The zone is to the left of the target - zone 3/L
|
499
|
+
c.right = targetRect.x - targetMargin[3];
|
500
|
+
break;
|
501
|
+
}
|
502
|
+
zonesToTry[i].constrainRect = c;
|
503
|
+
}
|
504
|
+
|
505
|
+
// Now try to constrain our result into each zone's constraintZone
|
506
|
+
for (let i = 0; i < zonesToTry.length; i++) {
|
507
|
+
const
|
508
|
+
{
|
509
|
+
zone,
|
510
|
+
edgeAlign,
|
511
|
+
constrainRect
|
512
|
+
} = zonesToTry[i],
|
513
|
+
edge = zoneEdges[zone];
|
514
|
+
|
515
|
+
if (matchSize) {
|
516
|
+
// If we are aligning to the requested edge, or it's opposite edge then
|
517
|
+
// match that edge size, else revert it to our own size
|
518
|
+
result[matchDimension] = edge === edges.theirEdge || edge == oppositeEdge[edges.theirEdge] ? targetRect[matchDimension] : me[matchDimension];
|
519
|
+
}
|
520
|
+
|
521
|
+
// Do a simple align to the current edge
|
522
|
+
result = result.alignTo({
|
523
|
+
target : targetRect,
|
524
|
+
edgeAlign,
|
525
|
+
targetMargin
|
526
|
+
});
|
527
|
+
|
528
|
+
let solution = result.constrainTo(constrainRect);
|
529
|
+
|
530
|
+
// As soon as we find a zone into which the result is willing to be constrained. return it
|
531
|
+
if (solution) {
|
532
|
+
solution.zone = zone;
|
533
|
+
solution.position = zoneNames[zone];
|
534
|
+
return solution;
|
535
|
+
}
|
536
|
+
}
|
537
|
+
}
|
538
|
+
|
539
|
+
return result;
|
540
|
+
}
|
541
|
+
|
542
|
+
getAnchorPoint(edgeZone, edgeOffset, edgeUnit, margin = emptyArray) {
|
543
|
+
const me = this;
|
544
|
+
|
545
|
+
let result;
|
546
|
+
|
547
|
+
// Edge zones go top, right, bottom, left
|
548
|
+
// Each one calculates the start point of that edge then moves along it by
|
549
|
+
// the edgeOffset, then moves *away* from it by the margin for that edge if there's a margin.
|
550
|
+
switch (edgeZone) {
|
551
|
+
case 0:
|
552
|
+
result = [me.x, me.y - (margin[0] || 0), me.width, 0];
|
553
|
+
break;
|
554
|
+
case 1:
|
555
|
+
result = [me.x + me.width + (margin[1] || 0), me.y, me.height, 1];
|
556
|
+
break;
|
557
|
+
case 2:
|
558
|
+
result = [me.x, me.y + me.height + (margin[2] || 0), me.width, 0];
|
559
|
+
break;
|
560
|
+
case 3:
|
561
|
+
result = [me.x - (margin[3] || 0), me.y, me.height, 1];
|
562
|
+
}
|
563
|
+
result[result[3]] += edgeUnit === '%' ? result[2] / 100 * edgeOffset : edgeOffset;
|
564
|
+
result.length = 2;
|
565
|
+
return result;
|
566
|
+
}
|
567
|
+
|
568
|
+
equals(other) {
|
569
|
+
return other instanceof DOMRect &&
|
570
|
+
other.x === this.x &&
|
571
|
+
other.y === this.y &&
|
572
|
+
other.height === this.height &&
|
573
|
+
other.width === this.width;
|
574
|
+
}
|
575
|
+
|
576
|
+
// For debugging purposes only
|
577
|
+
show(color = 'red') {
|
578
|
+
const div = document.createElement('div');
|
579
|
+
|
580
|
+
div.style = `
|
581
|
+
position:absolute;
|
582
|
+
transform:translate3d(${this.x}px, ${this.y}px, 0);
|
583
|
+
height:${this.height}px;
|
584
|
+
width:${this.width}px;
|
585
|
+
background-color:${color}
|
586
|
+
`;
|
587
|
+
document.body.appendChild(div);
|
588
|
+
setTimeout(() => div.remove(), 30000);
|
589
|
+
return div;
|
590
|
+
}
|
591
|
+
}
|