ember-attacher 1.3.0 → 2.0.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/.github/dependabot.yml +0 -3
- package/.github/workflows/ci.yml +2 -2
- package/CHANGELOG.md +20 -0
- package/README.md +44 -31
- package/addon/components/attach-popover.js +299 -232
- package/addon/components/attach-tooltip.js +17 -33
- package/addon/defaults.js +5 -4
- package/addon/styles/_mixins.scss +4 -6
- package/addon/styles/ember-attacher.scss +5 -0
- package/addon/templates/components/attach-popover.hbs +26 -22
- package/addon-test-support/is-visible.js +1 -1
- package/docs/upgrade-guide-2.0.md +43 -0
- package/index.js +0 -2
- package/package.json +42 -34
- package/app/styles/app.scss +0 -1
- package/yarn-error.log +0 -14650
|
@@ -1,102 +1,149 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { observes } from '@ember-decorators/object';
|
|
4
|
-
import { action, computed } from '@ember/object';
|
|
5
|
-
import Component from '@ember/component';
|
|
6
|
-
import DEFAULTS from '../defaults';
|
|
7
|
-
import layout from '../templates/components/attach-popover';
|
|
1
|
+
import { action } from '@ember/object';
|
|
2
|
+
import Component from '@glimmer/component';
|
|
8
3
|
import { cancel, debounce, later, next, run } from '@ember/runloop';
|
|
9
4
|
import { getOwner } from '@ember/application';
|
|
10
5
|
import { guidFor } from '@ember/object/internals';
|
|
11
|
-
import { htmlSafe, isHTMLSafe } from '@ember/
|
|
6
|
+
import { htmlSafe, isHTMLSafe } from '@ember/template';
|
|
12
7
|
import { stripInProduction } from 'ember-attacher/-debug/helpers';
|
|
13
|
-
import { warn } from '@ember/debug';
|
|
14
|
-
import { isEmpty } from '@ember/utils';
|
|
8
|
+
import { warn, assert } from '@ember/debug';
|
|
9
|
+
import { isEmpty, typeOf } from '@ember/utils';
|
|
10
|
+
import { autoUpdate, computePosition, arrow, flip } from '@floating-ui/dom';
|
|
11
|
+
import { buildWaiter } from '@ember/test-waiters';
|
|
12
|
+
import { tracked } from '@glimmer/tracking';
|
|
13
|
+
import DEFAULTS from '../defaults';
|
|
14
|
+
|
|
15
|
+
const animationTestWaiter = buildWaiter('attach-popover');
|
|
15
16
|
|
|
16
|
-
@classic
|
|
17
|
-
@templateLayout(layout)
|
|
18
|
-
@tagName('')
|
|
19
17
|
export default class AttachPopover extends Component {
|
|
18
|
+
@tracked parentNotFound = true;
|
|
19
|
+
@tracked parentElement = null;
|
|
20
|
+
@tracked _isStartingAnimation = false;
|
|
21
|
+
@tracked _arrowElement = null;
|
|
22
|
+
@tracked _currentTarget = null;
|
|
23
|
+
// This is set to true when the popover is shown in order to override lazyRender=false
|
|
24
|
+
@tracked _mustRender = false;
|
|
25
|
+
@tracked _transitionDuration = 0;
|
|
26
|
+
_floatingElement = null;
|
|
20
27
|
configKey = 'popover';
|
|
28
|
+
|
|
21
29
|
/**
|
|
22
30
|
* ================== PUBLIC CONFIG OPTIONS ==================
|
|
23
31
|
*/
|
|
32
|
+
get arrow() {
|
|
33
|
+
return this.args.arrow ?? this._config.arrow ?? DEFAULTS.arrow;
|
|
34
|
+
}
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
flip = DEFAULTS.flip;
|
|
29
|
-
hideDelay = DEFAULTS.hideDelay;
|
|
30
|
-
hideDuration = DEFAULTS.hideDuration;
|
|
31
|
-
hideOn = DEFAULTS.hideOn;
|
|
32
|
-
interactive = DEFAULTS.interactive;
|
|
33
|
-
isOffset = DEFAULTS.isOffset;
|
|
34
|
-
isShown = DEFAULTS.isShown;
|
|
35
|
-
lazyRender = DEFAULTS.lazyRender;
|
|
36
|
-
onChange = null;
|
|
37
|
-
placement = DEFAULTS.placement;
|
|
38
|
-
popperContainer = DEFAULTS.popperContainer;
|
|
39
|
-
popperOptions = DEFAULTS.popperOptions;
|
|
40
|
-
popperTarget = null;
|
|
41
|
-
renderInPlace = DEFAULTS.renderInPlace;
|
|
42
|
-
showDelay = DEFAULTS.showDelay;
|
|
43
|
-
showDuration = DEFAULTS.showDuration;
|
|
44
|
-
showOn = DEFAULTS.showOn;
|
|
45
|
-
style = DEFAULTS.style;
|
|
46
|
-
useCapture = DEFAULTS.useCapture;
|
|
36
|
+
get autoUpdate() {
|
|
37
|
+
return this.args.autoUpdate ?? this._config.autoUpdate ?? DEFAULTS.autoUpdate;
|
|
38
|
+
}
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
hide() {
|
|
51
|
-
this._hide();
|
|
40
|
+
get animation() {
|
|
41
|
+
return this.args.animation || this._config.animation || DEFAULTS.animation;
|
|
52
42
|
}
|
|
53
43
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this._enableEventListeners = api.enableEventListeners;
|
|
58
|
-
this._popperElement = api.popperElement;
|
|
59
|
-
this._update = api.update;
|
|
60
|
-
|
|
61
|
-
if (!this.isDestroying && !this.isDestroyed) {
|
|
62
|
-
if (this.registerAPI !== undefined) {
|
|
63
|
-
this.registerAPI(api);
|
|
64
|
-
}
|
|
44
|
+
get flip() {
|
|
45
|
+
return this.args.flip ?? this._config.flip ?? DEFAULTS.flip;
|
|
46
|
+
}
|
|
65
47
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
this._popperElement.style.visibility = 'hidden';
|
|
48
|
+
get hideDelay() {
|
|
49
|
+
return this.args.hideDelay ?? this._config.hideDelay ?? DEFAULTS.hideDelay;
|
|
50
|
+
}
|
|
70
51
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// The attachment will then correctly update its position from the first this._show()
|
|
75
|
-
this._popperElement.style.transform = null;
|
|
52
|
+
get hideDuration() {
|
|
53
|
+
return this.args.hideDuration ?? this._config.hideDuration ?? DEFAULTS.hideDuration;
|
|
54
|
+
}
|
|
76
55
|
|
|
77
|
-
|
|
78
|
-
|
|
56
|
+
get hideOn() {
|
|
57
|
+
return this.args.hideOn || this._config.hideOn || DEFAULTS.hideOn;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get interactive() {
|
|
61
|
+
return this.args.interactive ?? this._config.interactive ?? DEFAULTS.interactive;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get isOffset() {
|
|
65
|
+
return this.args.isOffset ?? this._config.isOffset ?? DEFAULTS.interactive;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get isShown() {
|
|
69
|
+
return this.args.isShown ?? this._config.isShown ?? DEFAULTS.isShown;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get lazyRender() {
|
|
73
|
+
return this.args.lazyRender ?? this._config.lazyRender ?? DEFAULTS.lazyRender;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get placement() {
|
|
77
|
+
return this.args.placement ?? this._config.placement ?? DEFAULTS.placement;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get floatingElementContainer() {
|
|
81
|
+
return this.args.floatingElementContainer || this._config.floatingElementContainer || DEFAULTS.floatingElementContainer;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get class() {
|
|
85
|
+
return this.args.class || this._config.class;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get floatingUiOptions() {
|
|
89
|
+
return this.args.floatingUiOptions || this._config.floatingUiOptions || DEFAULTS.floatingUiOptions;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get renderInPlace() {
|
|
93
|
+
return this.args.renderInPlace ?? this._config.renderInPlace ?? DEFAULTS.renderInPlace;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
get showDelay() {
|
|
97
|
+
return this.args.showDelay ?? this._config.showDelay ?? DEFAULTS.showDelay;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get showDuration() {
|
|
101
|
+
return this.args.showDuration ?? this._config.showDuration ?? DEFAULTS.showDuration;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get showOn() {
|
|
105
|
+
if (this.args.showOn === null) {
|
|
106
|
+
return null;
|
|
79
107
|
}
|
|
108
|
+
|
|
109
|
+
return this.args.showOn ?? this._config.showOn ?? DEFAULTS.showOn;
|
|
80
110
|
}
|
|
81
111
|
|
|
82
|
-
|
|
112
|
+
get style() {
|
|
113
|
+
return this.args.style ?? this._config.style ?? DEFAULTS.style;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get useCapture() {
|
|
117
|
+
return this.args.useCapture ?? this._config.useCapture ?? DEFAULTS.useCapture;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get isFillAnimation() {
|
|
121
|
+
return this.animation === 'fill';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get renderFloatingElement() {
|
|
125
|
+
return (this.renderInPlace || this._currentTarget) && (!this.lazyRender || this._mustRender);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get id() {
|
|
129
|
+
return this.args.id || `${guidFor(this)}-floating`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// The circle element needs a special duration that is slightly faster than the floating element's
|
|
83
133
|
// transition, this prevents text from appearing outside the circle as it fills the background
|
|
84
|
-
@computed('_transitionDuration')
|
|
85
134
|
get _circleTransitionDuration() {
|
|
86
135
|
return htmlSafe(
|
|
87
136
|
`transition-duration: ${Math.round(this._transitionDuration / 1.25)}ms`
|
|
88
137
|
);
|
|
89
138
|
}
|
|
90
139
|
|
|
91
|
-
@computed('class', 'arrow', 'animation', '_isStartingAnimation')
|
|
92
140
|
get _class() {
|
|
93
141
|
const showOrHideClass = `ember-attacher-${this._isStartingAnimation ? 'show' : 'hide'}`;
|
|
94
142
|
const arrowClass = `ember-attacher-${this.arrow ? 'with' : 'without'}-arrow`;
|
|
95
143
|
|
|
96
|
-
return `ember-attacher-${this.animation}
|
|
144
|
+
return [`ember-attacher-${this.animation}`, this.class || '', showOrHideClass, arrowClass].filter(Boolean).join(' ');
|
|
97
145
|
}
|
|
98
146
|
|
|
99
|
-
@computed('style', '_transitionDuration')
|
|
100
147
|
get _style() {
|
|
101
148
|
const style = this.style;
|
|
102
149
|
const transitionDuration = this._transitionDuration;
|
|
@@ -114,7 +161,6 @@ export default class AttachPopover extends Component {
|
|
|
114
161
|
return getOwner(this).resolveRegistration('config:environment').emberAttacher || {};
|
|
115
162
|
}
|
|
116
163
|
|
|
117
|
-
@computed('_envConfig', 'configKey')
|
|
118
164
|
get _config() {
|
|
119
165
|
return {
|
|
120
166
|
...this._envConfig,
|
|
@@ -122,7 +168,6 @@ export default class AttachPopover extends Component {
|
|
|
122
168
|
};
|
|
123
169
|
}
|
|
124
170
|
|
|
125
|
-
@computed('hideOn')
|
|
126
171
|
get _hideOn() {
|
|
127
172
|
let hideOn = this.hideOn;
|
|
128
173
|
|
|
@@ -133,63 +178,89 @@ export default class AttachPopover extends Component {
|
|
|
133
178
|
return hideOn === null ? [] : hideOn.split(' ');
|
|
134
179
|
}
|
|
135
180
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
= this.modifiers ? Object.assign({}, this.modifiers) : {};
|
|
181
|
+
get _middleware() {
|
|
182
|
+
// Copy the middleware since we might write to the provided array
|
|
183
|
+
const middleware
|
|
184
|
+
= this.args.middleware ? [...this.args.middleware] : [];
|
|
141
185
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
modifiers.arrow = { enabled: arrow };
|
|
186
|
+
if (this.arrow && this._arrowElement && !middleware.find(name => name === 'arrow')) {
|
|
187
|
+
middleware.push(arrow({ element: this._arrowElement }));
|
|
145
188
|
}
|
|
146
189
|
|
|
147
190
|
const flipString = this.flip;
|
|
148
191
|
if (flipString) {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
192
|
+
const flipOptions = { fallbackPlacements: flipString.split(' ') };
|
|
193
|
+
const flipMiddleware = middleware.find(name => name === 'flip');
|
|
194
|
+
if (!flipMiddleware) {
|
|
195
|
+
middleware.push(flip(flipOptions));
|
|
196
|
+
} else if (!flipMiddleware.fallbackPlacements) {
|
|
197
|
+
Object.assign({}, flipMiddleware, flipOptions);
|
|
155
198
|
}
|
|
156
199
|
}
|
|
157
200
|
|
|
158
|
-
return
|
|
201
|
+
return middleware;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
get _floatingElementContainer() {
|
|
205
|
+
const maybeContainer = this.floatingElementContainer;
|
|
206
|
+
const renderInPlace = this._renderInPlace;
|
|
207
|
+
let floatingElementContainer;
|
|
208
|
+
|
|
209
|
+
if (renderInPlace) {
|
|
210
|
+
floatingElementContainer = this.parentElement;
|
|
211
|
+
} else if (maybeContainer instanceof Element) {
|
|
212
|
+
floatingElementContainer = maybeContainer;
|
|
213
|
+
} else if (typeof maybeContainer === 'string') {
|
|
214
|
+
const selector = maybeContainer;
|
|
215
|
+
const possibleContainers = self.document.querySelectorAll(selector);
|
|
216
|
+
|
|
217
|
+
assert(`floatingElementContainer selector "${selector}" found `
|
|
218
|
+
+ `${possibleContainers.length} possible containers when there should be exactly 1`, possibleContainers.length === 1);
|
|
219
|
+
|
|
220
|
+
floatingElementContainer = possibleContainers[0];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return floatingElementContainer;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
get _renderInPlace() {
|
|
227
|
+
// self.document is undefined in Fastboot, so we have to render in
|
|
228
|
+
// place for the floating element to show up at all.
|
|
229
|
+
return self.document ? !!this.renderInPlace : true;
|
|
159
230
|
}
|
|
160
231
|
|
|
161
232
|
_setIsVisibleAfterDelay(isVisible, delay) {
|
|
162
|
-
if (!this.
|
|
233
|
+
if (!this._floatingElement) {
|
|
163
234
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
164
|
-
this.
|
|
235
|
+
this._setIsVisibleAfterDelay(isVisible, delay);
|
|
165
236
|
});
|
|
166
|
-
|
|
167
237
|
return;
|
|
168
238
|
}
|
|
169
|
-
const onChange = this.onChange;
|
|
239
|
+
const onChange = this.args.onChange;
|
|
170
240
|
|
|
171
241
|
if (delay) {
|
|
172
242
|
this._delayedVisibilityToggle = later(this, () => {
|
|
173
243
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
244
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
174
245
|
if (!this.isDestroyed && !this.isDestroying) {
|
|
175
|
-
this.
|
|
246
|
+
this._floatingElement.style.display = isVisible ? 'block' : 'none';
|
|
176
247
|
|
|
177
248
|
// Prevent jank by making the attachment invisible until positioned.
|
|
178
249
|
// The visibility style will be toggled by this._startShowAnimation()
|
|
179
|
-
this.
|
|
250
|
+
this._floatingElement.style.visibility = isVisible ? 'hidden' : '';
|
|
180
251
|
|
|
181
252
|
if (onChange) {
|
|
182
253
|
onChange(isVisible);
|
|
183
254
|
}
|
|
184
255
|
}
|
|
185
256
|
});
|
|
257
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
186
258
|
}, delay);
|
|
187
259
|
} else {
|
|
188
|
-
this.
|
|
189
|
-
|
|
260
|
+
this._floatingElement.style.display = isVisible ? 'block' : 'none';
|
|
190
261
|
// Prevent jank by making the attachment invisible until positioned.
|
|
191
262
|
// The visibility style will be toggled by this._startShowAnimation()
|
|
192
|
-
this.
|
|
263
|
+
this._floatingElement.style.visibility = isVisible ? 'hidden' : '';
|
|
193
264
|
|
|
194
265
|
if (onChange) {
|
|
195
266
|
onChange(isVisible);
|
|
@@ -197,10 +268,6 @@ export default class AttachPopover extends Component {
|
|
|
197
268
|
}
|
|
198
269
|
}
|
|
199
270
|
|
|
200
|
-
// This is set to true when the popover is shown in order to override lazyRender=false
|
|
201
|
-
_mustRender = false;
|
|
202
|
-
|
|
203
|
-
@computed('showOn')
|
|
204
271
|
get _showOn() {
|
|
205
272
|
let showOn = this.showOn;
|
|
206
273
|
|
|
@@ -211,26 +278,41 @@ export default class AttachPopover extends Component {
|
|
|
211
278
|
return showOn === null ? [] : showOn.split(' ');
|
|
212
279
|
}
|
|
213
280
|
|
|
214
|
-
|
|
281
|
+
// Exposed via the named yield to enable custom hide events
|
|
282
|
+
@action
|
|
283
|
+
hide() {
|
|
284
|
+
this._hide();
|
|
285
|
+
}
|
|
215
286
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
287
|
+
@action
|
|
288
|
+
onParentFinderInsert(element) {
|
|
289
|
+
this.parentElement = element.parentElement;
|
|
290
|
+
this._initializeAttacher();
|
|
291
|
+
}
|
|
219
292
|
|
|
220
|
-
|
|
221
|
-
|
|
293
|
+
@action
|
|
294
|
+
_ensureArgumentsAreValid() {
|
|
295
|
+
stripInProduction(() => {
|
|
296
|
+
if (this.arrow && this.isFillAnimation) {
|
|
297
|
+
warn('Animation: \'fill\' is not compatible with arrow: true', { id: 70015 });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (this.useCapture !== this._lastUseCaptureArgumentValue) {
|
|
301
|
+
warn(
|
|
302
|
+
'The value of the useCapture argument was mutated',
|
|
303
|
+
{ id: 'ember-attacher.use-capture-mutated' }
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
222
308
|
|
|
223
|
-
// Used to determine the attachments initial parent element
|
|
224
|
-
this._parentFinder = self.document ? self.document.createTextNode('') : '';
|
|
225
309
|
|
|
226
|
-
|
|
227
|
-
|
|
310
|
+
constructor() {
|
|
311
|
+
super(...arguments);
|
|
228
312
|
|
|
229
313
|
// The debounced _hide() and _show() are stored here so they can be cancelled when necessary
|
|
230
314
|
this._delayedVisibilityToggle = null;
|
|
231
315
|
|
|
232
|
-
this.id = this.id || `${guidFor(this)}-popper`;
|
|
233
|
-
|
|
234
316
|
// The final source of truth on whether or not all _hide() or _show() actions have completed
|
|
235
317
|
this._isHidden = true;
|
|
236
318
|
|
|
@@ -257,97 +339,12 @@ export default class AttachPopover extends Component {
|
|
|
257
339
|
this._show = this._show.bind(this);
|
|
258
340
|
this._showAfterDelay = this._showAfterDelay.bind(this);
|
|
259
341
|
|
|
260
|
-
this.
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
didReceiveAttrs() {
|
|
264
|
-
super.didReceiveAttrs(...arguments);
|
|
265
|
-
|
|
266
|
-
stripInProduction(() => {
|
|
267
|
-
// eslint-disable-next-line ember/no-attrs-in-components
|
|
268
|
-
const attrs = this.attrs || {};
|
|
269
|
-
const userDefaults = this._config;
|
|
270
|
-
|
|
271
|
-
let arrow;
|
|
272
|
-
if (attrs.arrow !== undefined) {
|
|
273
|
-
arrow = attrs.arrow.value;
|
|
274
|
-
} else if (userDefaults.arrow !== undefined) {
|
|
275
|
-
arrow = userDefaults.arrow;
|
|
276
|
-
} else {
|
|
277
|
-
arrow = DEFAULTS.arrow;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
let animation;
|
|
281
|
-
if (attrs.animation !== undefined) {
|
|
282
|
-
animation = attrs.animation.value;
|
|
283
|
-
} else if (userDefaults.animation !== undefined) {
|
|
284
|
-
animation = userDefaults.animation;
|
|
285
|
-
} else {
|
|
286
|
-
animation = DEFAULTS.animation;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (arrow && animation === 'fill') {
|
|
290
|
-
warn('Animation: \'fill\' is not compatible with arrow: true', { id: 70015 });
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
this._lastUseCaptureArgumentValue = this.useCapture;
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
didUpdateAttrs() {
|
|
298
|
-
super.didUpdateAttrs(...arguments);
|
|
299
|
-
|
|
300
|
-
stripInProduction(() => {
|
|
301
|
-
if (this.useCapture !== this._lastUseCaptureArgumentValue) {
|
|
302
|
-
warn(
|
|
303
|
-
'The value of the useCapture argument was mutated',
|
|
304
|
-
{ id: 'ember-attacher.use-capture-mutated' }
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
})
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
_setUserSuppliedDefaults() {
|
|
311
|
-
const userDefaults = this._config;
|
|
312
|
-
|
|
313
|
-
// Exit early if no custom defaults are found
|
|
314
|
-
if (!Object.keys(userDefaults).length) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// eslint-disable-next-line ember/no-attrs-in-components
|
|
319
|
-
const attrs = this.attrs || {};
|
|
320
|
-
|
|
321
|
-
for (const key in userDefaults) {
|
|
322
|
-
stripInProduction(() => {
|
|
323
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
324
|
-
if (!['popover','tooltip', 'class'].includes(key) && !DEFAULTS.hasOwnProperty(key)) {
|
|
325
|
-
warn(`Unknown property given as an ember-attacher default: ${key}`, { id: 700152 });
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// Don't override attrs manually passed into the component
|
|
330
|
-
if (attrs[key] === undefined) {
|
|
331
|
-
if (key === 'arrow') {
|
|
332
|
-
this.set('arrow', userDefaults[key]);
|
|
333
|
-
} else {
|
|
334
|
-
this[key] = userDefaults[key];
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
didInsertElement() {
|
|
341
|
-
super.didInsertElement(...arguments);
|
|
342
|
-
|
|
343
|
-
this._initializeAttacher();
|
|
342
|
+
this._lastUseCaptureArgumentValue = this.useCapture;
|
|
344
343
|
}
|
|
345
344
|
|
|
346
345
|
_initializeAttacher() {
|
|
347
346
|
this._removeEventListeners();
|
|
348
|
-
|
|
349
|
-
this.set('_currentTarget', this.popperTarget || this._parentFinder.parentNode);
|
|
350
|
-
|
|
347
|
+
this._currentTarget = this.args.explicitTarget || this.parentElement;
|
|
351
348
|
this._addListenersForShowEvents();
|
|
352
349
|
|
|
353
350
|
if (!this._isHidden || this.isShown) {
|
|
@@ -360,7 +357,6 @@ export default class AttachPopover extends Component {
|
|
|
360
357
|
}
|
|
361
358
|
|
|
362
359
|
_addListenersForShowEvents() {
|
|
363
|
-
|
|
364
360
|
if (!this._currentTarget) {
|
|
365
361
|
return;
|
|
366
362
|
}
|
|
@@ -372,10 +368,10 @@ export default class AttachPopover extends Component {
|
|
|
372
368
|
});
|
|
373
369
|
}
|
|
374
370
|
|
|
375
|
-
|
|
376
|
-
super.
|
|
371
|
+
willDestroy() {
|
|
372
|
+
super.willDestroy(...arguments);
|
|
377
373
|
|
|
378
|
-
|
|
374
|
+
this._cancelAnimation();
|
|
379
375
|
cancel(this._delayedVisibilityToggle);
|
|
380
376
|
|
|
381
377
|
this._removeEventListeners();
|
|
@@ -386,7 +382,6 @@ export default class AttachPopover extends Component {
|
|
|
386
382
|
document.removeEventListener(eventType, this._hideListenersOnDocumentByEvent[eventType], this.useCapture);
|
|
387
383
|
delete this._hideListenersOnDocumentByEvent[eventType];
|
|
388
384
|
});
|
|
389
|
-
|
|
390
385
|
if (!this._currentTarget) {
|
|
391
386
|
return;
|
|
392
387
|
}
|
|
@@ -399,13 +394,13 @@ export default class AttachPopover extends Component {
|
|
|
399
394
|
});
|
|
400
395
|
}
|
|
401
396
|
|
|
402
|
-
@
|
|
403
|
-
|
|
397
|
+
@action
|
|
398
|
+
onTargetOrTriggerChange() {
|
|
404
399
|
this._initializeAttacher();
|
|
405
400
|
}
|
|
406
401
|
|
|
407
|
-
@
|
|
408
|
-
|
|
402
|
+
@action
|
|
403
|
+
onIsShownChange() {
|
|
409
404
|
const isShown = this.isShown;
|
|
410
405
|
|
|
411
406
|
if (isShown === true && this._isHidden) {
|
|
@@ -426,7 +421,7 @@ export default class AttachPopover extends Component {
|
|
|
426
421
|
_showAfterDelay() {
|
|
427
422
|
cancel(this._delayedVisibilityToggle);
|
|
428
423
|
|
|
429
|
-
this.
|
|
424
|
+
this._mustRender = true;
|
|
430
425
|
|
|
431
426
|
this._addListenersForHideEvents();
|
|
432
427
|
|
|
@@ -436,13 +431,13 @@ export default class AttachPopover extends Component {
|
|
|
436
431
|
}
|
|
437
432
|
|
|
438
433
|
_show() {
|
|
439
|
-
|
|
434
|
+
this._cancelAnimation();
|
|
440
435
|
|
|
441
436
|
if (!this._currentTarget) {
|
|
442
437
|
return;
|
|
443
438
|
}
|
|
444
439
|
|
|
445
|
-
this.
|
|
440
|
+
this._mustRender = true;
|
|
446
441
|
|
|
447
442
|
// Make the attachment visible immediately so transition animations can take place
|
|
448
443
|
this._setIsVisibleAfterDelay(true, 0);
|
|
@@ -457,43 +452,44 @@ export default class AttachPopover extends Component {
|
|
|
457
452
|
// All included animations set opaque: 0, so the attachment is still effectively hidden until
|
|
458
453
|
// the final RAF occurs.
|
|
459
454
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
455
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
460
456
|
if (this.isDestroyed || this.isDestroying || !this._currentTarget) {
|
|
461
457
|
return;
|
|
462
458
|
}
|
|
463
459
|
|
|
464
|
-
const
|
|
460
|
+
const floatingElement = this._floatingElement;
|
|
465
461
|
|
|
466
462
|
// Wait until the element is visible before continuing
|
|
467
|
-
if (!
|
|
468
|
-
this.
|
|
469
|
-
|
|
463
|
+
if (!floatingElement || floatingElement.style.display === 'none') {
|
|
464
|
+
this._startShowAnimation();
|
|
470
465
|
return;
|
|
471
466
|
}
|
|
472
467
|
|
|
473
|
-
this._enableEventListeners();
|
|
474
468
|
this._update();
|
|
475
469
|
|
|
476
470
|
// Wait for the above positioning to take effect before starting the show animation,
|
|
477
471
|
// else the positioning itself will be animated, causing animation glitches.
|
|
478
472
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
473
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
479
474
|
if (this.isDestroyed || this.isDestroying || !this._currentTarget) {
|
|
480
475
|
return;
|
|
481
476
|
}
|
|
482
|
-
|
|
483
477
|
run(() => {
|
|
484
478
|
if (this.isDestroyed || this.isDestroying || !this._currentTarget) {
|
|
485
479
|
return;
|
|
486
480
|
}
|
|
487
|
-
// Make the
|
|
488
|
-
|
|
489
|
-
this.
|
|
490
|
-
this.
|
|
491
|
-
|
|
481
|
+
// Make the floating element visible now that it has been positioned
|
|
482
|
+
floatingElement.style.visibility = '';
|
|
483
|
+
this._transitionDuration = parseInt(this.showDuration);
|
|
484
|
+
this._isStartingAnimation = true;
|
|
485
|
+
floatingElement.setAttribute('aria-hidden', 'false');
|
|
492
486
|
});
|
|
493
487
|
|
|
494
488
|
this._isHidden = false;
|
|
495
489
|
});
|
|
490
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
496
491
|
});
|
|
492
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
497
493
|
}
|
|
498
494
|
|
|
499
495
|
/**
|
|
@@ -509,18 +505,20 @@ export default class AttachPopover extends Component {
|
|
|
509
505
|
}
|
|
510
506
|
|
|
511
507
|
_hide() {
|
|
512
|
-
if (!this.
|
|
508
|
+
if (!this._floatingElement) {
|
|
513
509
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
510
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
514
511
|
this._animationTimeout = this._hide();
|
|
515
512
|
});
|
|
513
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
516
514
|
return;
|
|
517
515
|
}
|
|
518
516
|
|
|
519
|
-
|
|
517
|
+
this._cancelAnimation();
|
|
520
518
|
|
|
521
519
|
this._removeListenersForHideEvents();
|
|
522
|
-
|
|
523
520
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
521
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
524
522
|
// Avoid a race condition where we attempt to hide after the component is being destroyed.
|
|
525
523
|
if (this.isDestroyed || this.isDestroying) {
|
|
526
524
|
return;
|
|
@@ -533,18 +531,16 @@ export default class AttachPopover extends Component {
|
|
|
533
531
|
return;
|
|
534
532
|
}
|
|
535
533
|
|
|
536
|
-
this.
|
|
537
|
-
this.
|
|
538
|
-
this.
|
|
539
|
-
|
|
534
|
+
this._transitionDuration = hideDuration;
|
|
535
|
+
this._isStartingAnimation = false;
|
|
536
|
+
this._floatingElement.setAttribute('aria-hidden', 'true');
|
|
540
537
|
// Wait for any animations to complete before hiding the attachment
|
|
541
538
|
this._setIsVisibleAfterDelay(false, hideDuration);
|
|
542
539
|
});
|
|
543
540
|
|
|
544
|
-
this._disableEventListeners();
|
|
545
|
-
|
|
546
541
|
this._isHidden = true;
|
|
547
542
|
});
|
|
543
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
548
544
|
}
|
|
549
545
|
|
|
550
546
|
/**
|
|
@@ -628,10 +624,10 @@ export default class AttachPopover extends Component {
|
|
|
628
624
|
return;
|
|
629
625
|
}
|
|
630
626
|
|
|
631
|
-
// If cursor is not on the attachment or target, hide the
|
|
627
|
+
// If cursor is not on the attachment or target, hide the floating element
|
|
632
628
|
if (!target.contains(event.target)
|
|
633
629
|
&& !(this.isOffset && this._isCursorBetweenTargetAndAttachment(event))
|
|
634
|
-
&& (this.
|
|
630
|
+
&& (this._floatingElement && !this._floatingElement.contains(event.target))) {
|
|
635
631
|
// Remove this listener before hiding the attachment
|
|
636
632
|
delete this._hideListenersOnDocumentByEvent.mousemove;
|
|
637
633
|
document.removeEventListener('mousemove', this._hideIfMouseOutsideTargetOrAttachment, this.useCapture);
|
|
@@ -648,7 +644,7 @@ export default class AttachPopover extends Component {
|
|
|
648
644
|
|
|
649
645
|
const { clientX, clientY } = event;
|
|
650
646
|
|
|
651
|
-
const attachmentPosition = this.
|
|
647
|
+
const attachmentPosition = this._floatingElement.getBoundingClientRect();
|
|
652
648
|
const targetPosition = this._currentTarget.getBoundingClientRect();
|
|
653
649
|
|
|
654
650
|
const isBetweenLeftAndRight = clientX > Math.min(attachmentPosition.left, targetPosition.left)
|
|
@@ -692,7 +688,7 @@ export default class AttachPopover extends Component {
|
|
|
692
688
|
const targetReceivedClick = this._currentTarget.contains(event.target);
|
|
693
689
|
|
|
694
690
|
if (this.interactive) {
|
|
695
|
-
if (!targetReceivedClick && !this.
|
|
691
|
+
if (!targetReceivedClick && !this._floatingElement.contains(event.target)) {
|
|
696
692
|
this._hideAfterDelay();
|
|
697
693
|
}
|
|
698
694
|
} else if (!targetReceivedClick) {
|
|
@@ -718,7 +714,7 @@ export default class AttachPopover extends Component {
|
|
|
718
714
|
const targetContainsFocus = this._currentTarget.contains(event.relatedTarget);
|
|
719
715
|
|
|
720
716
|
if (this.interactive) {
|
|
721
|
-
if (!targetContainsFocus && !this.
|
|
717
|
+
if (!targetContainsFocus && !this._floatingElement.contains(event.relatedTarget)) {
|
|
722
718
|
this._hideAfterDelay();
|
|
723
719
|
}
|
|
724
720
|
} else if (!targetContainsFocus) {
|
|
@@ -762,4 +758,75 @@ export default class AttachPopover extends Component {
|
|
|
762
758
|
}
|
|
763
759
|
});
|
|
764
760
|
}
|
|
761
|
+
|
|
762
|
+
@action
|
|
763
|
+
didInsertFloatingElement(floatingElement) {
|
|
764
|
+
this._floatingElement = floatingElement;
|
|
765
|
+
|
|
766
|
+
if (this.renderInPlace) {
|
|
767
|
+
this.parentElement = floatingElement.parentElement;
|
|
768
|
+
this._initializeAttacher();
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
@action
|
|
773
|
+
didInsertArrow(element) {
|
|
774
|
+
this._arrowElement = element;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
@action
|
|
778
|
+
onOptionsChange() {
|
|
779
|
+
this._ensureArgumentsAreValid();
|
|
780
|
+
this._update();
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
@action
|
|
784
|
+
willDestroyFloatingElement() {
|
|
785
|
+
this._cleanup?.();
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
_update() {
|
|
789
|
+
this._cleanup?.();
|
|
790
|
+
if (this.autoUpdate) {
|
|
791
|
+
this._cleanup = autoUpdate(
|
|
792
|
+
this._currentTarget,
|
|
793
|
+
this._floatingElement,
|
|
794
|
+
this._updatePosition,
|
|
795
|
+
typeOf(this.autoUpdate) === 'object' ? this.autoUpdate : undefined
|
|
796
|
+
);
|
|
797
|
+
} else {
|
|
798
|
+
this._updatePosition();
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
@action
|
|
803
|
+
_updatePosition() {
|
|
804
|
+
const computePositionToken = animationTestWaiter.beginAsync();
|
|
805
|
+
computePosition(this._currentTarget, this._floatingElement, {
|
|
806
|
+
...this.floatingUiOptions,
|
|
807
|
+
middleware: this._middleware,
|
|
808
|
+
placement: this.placement
|
|
809
|
+
}).then(({ x, y, placement, middlewareData }) => {
|
|
810
|
+
animationTestWaiter.endAsync(computePositionToken);
|
|
811
|
+
Object.assign(this._floatingElement.style, { left: `${x}px`, top: `${y}px`, });
|
|
812
|
+
|
|
813
|
+
if (middlewareData.arrow) {
|
|
814
|
+
const { x, y } = middlewareData.arrow;
|
|
815
|
+
|
|
816
|
+
Object.assign(this._arrowElement.style, {
|
|
817
|
+
left: x != null ? `${x}px` : '',
|
|
818
|
+
top: y != null ? `${y}px` : '',
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
this._floatingElement.setAttribute('x-placement', placement);
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
_cancelAnimation() {
|
|
826
|
+
cancelAnimationFrame(this._animationTimeout);
|
|
827
|
+
|
|
828
|
+
if (animationTestWaiter.items.get(this._animationTimeout)) {
|
|
829
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
765
832
|
}
|