Skip to content

Commit 6fa77ca

Browse files
committed
Feat: finish lookup table for anchor backup positions
1 parent 19301b7 commit 6fa77ca

File tree

1 file changed

+105
-92
lines changed

1 file changed

+105
-92
lines changed

src/behaviors/attach/attach.js

Lines changed: 105 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,29 @@ const defaultSettings = {
1616
// [`position1', 'position2'] - only use specific fallback positions
1717
fallbackStrategy: 'adjacent',
1818

19+
// whether to add a pointing arrow
20+
arrow: true,
21+
22+
offset: 0, // distance offset from calculated position
23+
distance: 14, // distance away from pointing to
24+
25+
alwaysShow: false, // always show element regardless of whether it fits
26+
anchorName: 'anchor-{count}', // name of anchor
27+
28+
moveElement: true, // whether to move element to same positioning context
29+
30+
observeChanges: true, // whether to observe changes and move element if it no longer fits
31+
containToScroll: true, // whether to contain element to its scroll container
32+
};
33+
34+
const createBehavior = ({ $, $el, el, self, cache, settings, classNames, error, debug, index, warn }) => ({
35+
anchorName: null,
36+
1937
// X X X
2038
// X X X
2139
// X X X
2240
// list of positions permitted to try
23-
positions: [
41+
allPositions: [
2442
'top left',
2543
'top',
2644
'top right', // TOP SIDE
@@ -33,85 +51,66 @@ const defaultSettings = {
3351
'left bottom',
3452
'left',
3553
'left top', // LEFT SIDE
36-
'center', // INSIDE
3754
],
3855

39-
// whether to add a pointing arrow
40-
arrow: true,
41-
42-
offset: 0, // distance offset from calculated position
43-
distance: 14, // distance away from pointing to
44-
45-
alwaysShow: false, // always show element regardless of whether it fits
46-
anchorName: 'anchor-{count}', // name of anchor
47-
48-
moveElement: true, // whether to move element to same positioning context
49-
50-
observeChanges: true, // whether to observe changes and move element if it no longer fits
51-
containToScroll: true, // whether to contain element to its scroll container
52-
};
53-
54-
const createBehavior = ({ $, $el, el, self, cache, settings, classNames, error, debug, index, warn }) => ({
55-
anchorName: null,
56+
// map of starting position to fallbacks using index of 'allPositions'
57+
// to make this less confusing it is 1-indexed.
58+
59+
/* 1 | 2 | 3
60+
-----------
61+
12 | | 4
62+
| |
63+
11 | | 5
64+
| |
65+
10 | | 6
66+
-----------
67+
9 | 8 | 7
68+
*/
69+
adjacentFallbacks: {
70+
'top left': [2, 3, 12, 4, 11, 5, 10, 6, 9, 8, 7],
71+
'top': [1, 3, 11, 5, 12, 4, 10, 6, 8, 9, 7],
72+
'top right': [2, 1, 4, 12, 5, 11, 6, 10, 7, 8, 9],
73+
'right top': [5, 6, 3, 7, 2, 8, 1, 9, 12, 11, 10],
74+
'right': [4, 6, 2, 8, 3, 7, 1, 9, 11, 12, 10],
75+
'right bottom': [5, 4, 7, 3, 8, 2, 9, 1, 10, 11, 12],
76+
'bottom right': [8, 9, 6, 10, 5, 11, 4, 12, 3, 2, 1],
77+
'bottom': [7, 9, 5, 11, 6, 10, 4, 12, 2, 3, 1],
78+
'bottom left': [8, 7, 10, 6, 11, 5, 12, 4, 1, 2, 3],
79+
'left bottom': [11, 12, 9, 1, 8, 2, 7, 3, 6, 4, 4],
80+
'left': [10, 12, 8, 2, 9, 1, 7, 3, 5, 4, 6],
81+
'left top': [11, 10, 1, 9, 2, 8, 3, 7, 4, 5, 6],
82+
},
83+
oppositeFallbacks: {
84+
'top left': [9, 2, 8, 3, 7, 12, 4, 11, 5, 10, 6],
85+
'top': [8, 1, 9, 3, 7, 11, 5, 12, 4, 10, 6],
86+
'top right': [7, 2, 8, 1, 9, 4, 12, 5, 11, 6, 10],
87+
'right top': [12, 5, 11, 6, 10, 3, 7, 2, 8, 1, 9],
88+
'right': [11, 4, 12, 6, 10, 2, 8, 3, 7, 1, 9],
89+
'right bottom': [10, 5, 11, 4, 12, 7, 3, 8, 2, 9, 1],
90+
'bottom right': [3, 8, 2, 9, 1, 6, 10, 5, 11, 4, 12],
91+
'bottom': [2, 7, 3, 9, 1, 5, 11, 6, 10, 4, 12],
92+
'bottom left': [1, 8, 2, 7, 3, 10, 6, 11, 5, 12, 4],
93+
'left bottom': [6, 11, 5, 12, 4, 9, 1, 8, 2, 7, 3],
94+
'left': [5, 10, 6, 12, 4, 2, 8, 9, 1, 7, 3],
95+
'left top': [4, 11, 5, 10, 6, 1, 9, 2, 8, 3, 7],
96+
},
5697

98+
// mapping of position name to css using shorthand
99+
// see <decodeCSSShorthand>
57100
positionMapping: {
58-
'top left': {
59-
'inset-block-end': 'anchor(top)',
60-
'inset-inline-start': 'anchor(left)',
61-
},
62-
'top': {
63-
'inset-block-end': 'anchor(top)',
64-
'inset-inline-start': 'anchor(center)',
65-
'translate': '-50% 0',
66-
},
67-
'top right': {
68-
'inset-block-end': 'anchor(top)',
69-
'inset-inline-end': 'anchor(right)',
70-
},
71-
'right bottom': {
72-
'inset-inline-start': 'anchor(right)',
73-
'inset-block-end': 'anchor(bottom)',
74-
},
75-
'right': {
76-
'inset-inline-start': 'anchor(right)',
77-
'inset-block-start': 'anchor(center)',
78-
'translate': '0 -50%',
79-
},
80-
'right top': {
81-
'inset-inline-start': 'anchor(right)',
82-
'inset-block-start': 'anchor(top)',
83-
},
84-
'bottom right': {
85-
'inset-block-start': 'anchor(bottom)',
86-
'inset-inline-end': 'anchor(right)',
87-
},
88-
'bottom': {
89-
'inset-block-start': 'anchor(bottom)',
90-
'inset-inline-start': 'anchor(center)',
91-
'translate': '-50% 0',
92-
},
93-
'bottom left': {
94-
'inset-block-start': 'anchor(bottom)',
95-
'inset-inline-start': 'anchor(left)',
96-
},
97-
'left bottom': {
98-
'inset-inline-end': 'anchor(left)',
99-
'inset-block-end': 'anchor(bottom)',
100-
},
101-
'left': {
102-
'inset-inline-end': 'anchor(left)',
103-
'inset-block-start': 'anchor(center)',
104-
'translate': '0 -50%',
105-
},
106-
'left top': {
107-
'inset-inline-end': 'anchor(left)',
108-
'inset-block-start': 'anchor(top)',
109-
},
110-
'center': {
111-
'inset-block-start': 'anchor(center)',
112-
'inset-inline-start': 'anchor(center)',
113-
'translate': '-50% -50%',
114-
},
101+
'top left': { ibe: 't', iis: 'l' },
102+
'top': { ibe: 't', iis: 'c', tr: 'ox' },
103+
'top right': { ibe: 't', iie: 'r' },
104+
'right top': { iis: 'r', ibs: 't' },
105+
'right': { iis: 'r', ibs: 'c', tr: 'oy' },
106+
'right bottom': { iis: 'r', ibe: 'b' },
107+
'bottom right': { ibs: 'b', iie: 'r' },
108+
'bottom': { ibs: 'b', iis: 'c', tr: 'ox' },
109+
'bottom left': { ibs: 'b', iis: 'l' },
110+
'left bottom': { iie: 'l', ibe: 'b' },
111+
'left': { iie: 'l', ibs: 'c', tr: 'oy' },
112+
'left top': { iie: 'l', ibs: 't' },
113+
'center': { ibs: 'c', iis: 'c', tr: 'o' },
115114
},
116115

117116
initialize() {
@@ -139,7 +138,7 @@ const createBehavior = ({ $, $el, el, self, cache, settings, classNames, error,
139138
},
140139

141140
getPositioningCSS(position = settings.position) {
142-
const positionCSS = self.positionMapping[position] || {};
141+
const positionCSS = self.getDecodedPositionCSS(position);
143142
const positionReset = {
144143
'inset-block-start': null,
145144
'inset-block-end': null,
@@ -153,6 +152,33 @@ const createBehavior = ({ $, $el, el, self, cache, settings, classNames, error,
153152
};
154153
},
155154

155+
// save some filesize on string literals
156+
getDecodedPositionCSS(position) {
157+
const shorthands = {
158+
ibs: 'inset-block-start',
159+
ibe: 'inset-block-end',
160+
iis: 'inset-inline-start',
161+
iie: 'inset-inline-end',
162+
t: 'anchor(top)',
163+
l: 'anchor(left)',
164+
b: 'anchor(bottom)',
165+
r: 'anchor(right)',
166+
c: 'anchor(center)',
167+
tr: 'translate',
168+
ox: '-50% 0', // offset x
169+
oy: '0 -50%', // offset y
170+
o: '-50% -50%', // offset
171+
};
172+
let css = self.positionMapping[position] || {};
173+
let outputCSS = {};
174+
each(css, (thisValue, thisProp) => {
175+
const prop = shorthands[thisProp] || thisProp;
176+
const value = shorthands[thisValue] || thisValue;
177+
outputCSS[prop] = value;
178+
});
179+
return outputCSS;
180+
},
181+
156182
setAnchorName() {
157183
const $anchor = self.getAnchor();
158184
if (!$anchor.exists()) {
@@ -176,25 +202,12 @@ const createBehavior = ({ $, $el, el, self, cache, settings, classNames, error,
176202
}
177203
},
178204

205+
// fallback order is implemented as a lookup table
206+
// as the algorithm can get fairly complex for 'adjacent' / 'opposite'
179207
getFallbackOrder() {
180208
if (settings.fallbackStrategy) {
181209
return settings.fallbackStrategy;
182210
}
183-
const positions = position.split(' ');
184-
const verticalPosition = positions[0];
185-
const horizontalPosition = positions[1] || 'center';
186-
const opposite = {
187-
top: 'bottom',
188-
bottom: 'top',
189-
left: 'right',
190-
right: 'left',
191-
};
192-
const adjacent = {
193-
left: 'center',
194-
center: 'right',
195-
right: 'left',
196-
};
197-
// get positions sorted by respected strategy
198211
},
199212

200213
getNextPosition(position = settings.position) {

0 commit comments

Comments
 (0)