ember-attacher 1.3.0 → 2.0.2
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/.idea/ember-attacher.iml +29 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/CHANGELOG.md +36 -0
- package/README.md +49 -33
- package/addon/components/attach-popover.js +310 -234
- package/addon/components/attach-tooltip.js +17 -33
- package/addon/defaults.js +7 -5
- 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 +45 -39
- package/.github/ISSUE_TEMPLATE.md +0 -3
- package/.github/dependabot.yml +0 -74
- package/.github/workflows/ci.yml +0 -221
- package/app/styles/app.scss +0 -1
- package/config/dependency-lint.js +0 -5
- package/config/environment.js +0 -6
- package/yarn-error.log +0 -14650
|
@@ -1,102 +1,153 @@
|
|
|
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, limitShift, shift } 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.isOffset;
|
|
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
|
+
get overflowPadding() {
|
|
133
|
+
return this.args.overflowPadding ?? this._config.overflowPadding ?? DEFAULTS.overflowPadding;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The circle element needs a special duration that is slightly faster than the floating element's
|
|
83
137
|
// transition, this prevents text from appearing outside the circle as it fills the background
|
|
84
|
-
@computed('_transitionDuration')
|
|
85
138
|
get _circleTransitionDuration() {
|
|
86
139
|
return htmlSafe(
|
|
87
140
|
`transition-duration: ${Math.round(this._transitionDuration / 1.25)}ms`
|
|
88
141
|
);
|
|
89
142
|
}
|
|
90
143
|
|
|
91
|
-
@computed('class', 'arrow', 'animation', '_isStartingAnimation')
|
|
92
144
|
get _class() {
|
|
93
145
|
const showOrHideClass = `ember-attacher-${this._isStartingAnimation ? 'show' : 'hide'}`;
|
|
94
146
|
const arrowClass = `ember-attacher-${this.arrow ? 'with' : 'without'}-arrow`;
|
|
95
147
|
|
|
96
|
-
return `ember-attacher-${this.animation}
|
|
148
|
+
return [`ember-attacher-${this.animation}`, this.class || '', showOrHideClass, arrowClass].filter(Boolean).join(' ');
|
|
97
149
|
}
|
|
98
150
|
|
|
99
|
-
@computed('style', '_transitionDuration')
|
|
100
151
|
get _style() {
|
|
101
152
|
const style = this.style;
|
|
102
153
|
const transitionDuration = this._transitionDuration;
|
|
@@ -114,7 +165,6 @@ export default class AttachPopover extends Component {
|
|
|
114
165
|
return getOwner(this).resolveRegistration('config:environment').emberAttacher || {};
|
|
115
166
|
}
|
|
116
167
|
|
|
117
|
-
@computed('_envConfig', 'configKey')
|
|
118
168
|
get _config() {
|
|
119
169
|
return {
|
|
120
170
|
...this._envConfig,
|
|
@@ -122,7 +172,6 @@ export default class AttachPopover extends Component {
|
|
|
122
172
|
};
|
|
123
173
|
}
|
|
124
174
|
|
|
125
|
-
@computed('hideOn')
|
|
126
175
|
get _hideOn() {
|
|
127
176
|
let hideOn = this.hideOn;
|
|
128
177
|
|
|
@@ -133,63 +182,94 @@ export default class AttachPopover extends Component {
|
|
|
133
182
|
return hideOn === null ? [] : hideOn.split(' ');
|
|
134
183
|
}
|
|
135
184
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const modifiers
|
|
140
|
-
= this.modifiers ? Object.assign({}, this.modifiers) : {};
|
|
141
|
-
|
|
142
|
-
const arrow = this.arrow;
|
|
143
|
-
if (typeof(arrow) === 'boolean' && !modifiers.arrow) {
|
|
144
|
-
modifiers.arrow = { enabled: arrow };
|
|
145
|
-
}
|
|
185
|
+
get _middleware() {
|
|
186
|
+
// Copy the middleware since we might write to the provided array
|
|
187
|
+
const middleware = this.args.middleware ? [...this.args.middleware] : [];
|
|
146
188
|
|
|
147
189
|
const flipString = this.flip;
|
|
148
190
|
if (flipString) {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
191
|
+
const flipOptions = { fallbackPlacements: flipString.split(' ') };
|
|
192
|
+
const flipMiddleware = middleware.find(name => name === 'flip');
|
|
193
|
+
if (!flipMiddleware) {
|
|
194
|
+
middleware.push(flip(flipOptions));
|
|
195
|
+
} else if (!flipMiddleware.fallbackPlacements) {
|
|
196
|
+
Object.assign({}, flipMiddleware, flipOptions);
|
|
155
197
|
}
|
|
198
|
+
} else if (this.overflowPadding !== false) {
|
|
199
|
+
middleware.push(flip());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (this.overflowPadding !== false && !middleware.find(name => name === 'shift')) {
|
|
203
|
+
middleware.push(shift({ limiter: limitShift(), padding: this.overflowPadding }))
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (this.arrow && this._arrowElement && !middleware.find(name => name === 'arrow')) {
|
|
207
|
+
middleware.push(arrow({ element: this._arrowElement }));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return middleware;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
get _floatingElementContainer() {
|
|
214
|
+
const maybeContainer = this.floatingElementContainer;
|
|
215
|
+
const renderInPlace = this._renderInPlace;
|
|
216
|
+
let floatingElementContainer;
|
|
217
|
+
|
|
218
|
+
if (renderInPlace) {
|
|
219
|
+
floatingElementContainer = this.parentElement;
|
|
220
|
+
} else if (maybeContainer instanceof Element) {
|
|
221
|
+
floatingElementContainer = maybeContainer;
|
|
222
|
+
} else if (typeof maybeContainer === 'string') {
|
|
223
|
+
const selector = maybeContainer;
|
|
224
|
+
const possibleContainers = self.document.querySelectorAll(selector);
|
|
225
|
+
|
|
226
|
+
assert(`floatingElementContainer selector "${selector}" found `
|
|
227
|
+
+ `${possibleContainers.length} possible containers when there should be exactly 1`, possibleContainers.length === 1);
|
|
228
|
+
|
|
229
|
+
floatingElementContainer = possibleContainers[0];
|
|
156
230
|
}
|
|
157
231
|
|
|
158
|
-
return
|
|
232
|
+
return floatingElementContainer;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
get _renderInPlace() {
|
|
236
|
+
// self.document is undefined in Fastboot, so we have to render in
|
|
237
|
+
// place for the floating element to show up at all.
|
|
238
|
+
return self.document ? !!this.renderInPlace : true;
|
|
159
239
|
}
|
|
160
240
|
|
|
161
241
|
_setIsVisibleAfterDelay(isVisible, delay) {
|
|
162
|
-
if (!this.
|
|
242
|
+
if (!this._floatingElement) {
|
|
163
243
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
164
|
-
this.
|
|
244
|
+
this._setIsVisibleAfterDelay(isVisible, delay);
|
|
165
245
|
});
|
|
166
|
-
|
|
167
246
|
return;
|
|
168
247
|
}
|
|
169
|
-
const onChange = this.onChange;
|
|
248
|
+
const onChange = this.args.onChange;
|
|
170
249
|
|
|
171
250
|
if (delay) {
|
|
172
251
|
this._delayedVisibilityToggle = later(this, () => {
|
|
173
252
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
253
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
174
254
|
if (!this.isDestroyed && !this.isDestroying) {
|
|
175
|
-
this.
|
|
255
|
+
this._floatingElement.style.display = isVisible ? 'block' : 'none';
|
|
176
256
|
|
|
177
257
|
// Prevent jank by making the attachment invisible until positioned.
|
|
178
258
|
// The visibility style will be toggled by this._startShowAnimation()
|
|
179
|
-
this.
|
|
259
|
+
this._floatingElement.style.visibility = isVisible ? 'hidden' : '';
|
|
180
260
|
|
|
181
261
|
if (onChange) {
|
|
182
262
|
onChange(isVisible);
|
|
183
263
|
}
|
|
184
264
|
}
|
|
185
265
|
});
|
|
266
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
186
267
|
}, delay);
|
|
187
268
|
} else {
|
|
188
|
-
this.
|
|
189
|
-
|
|
269
|
+
this._floatingElement.style.display = isVisible ? 'block' : 'none';
|
|
190
270
|
// Prevent jank by making the attachment invisible until positioned.
|
|
191
271
|
// The visibility style will be toggled by this._startShowAnimation()
|
|
192
|
-
this.
|
|
272
|
+
this._floatingElement.style.visibility = isVisible ? 'hidden' : '';
|
|
193
273
|
|
|
194
274
|
if (onChange) {
|
|
195
275
|
onChange(isVisible);
|
|
@@ -197,10 +277,6 @@ export default class AttachPopover extends Component {
|
|
|
197
277
|
}
|
|
198
278
|
}
|
|
199
279
|
|
|
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
280
|
get _showOn() {
|
|
205
281
|
let showOn = this.showOn;
|
|
206
282
|
|
|
@@ -211,26 +287,41 @@ export default class AttachPopover extends Component {
|
|
|
211
287
|
return showOn === null ? [] : showOn.split(' ');
|
|
212
288
|
}
|
|
213
289
|
|
|
214
|
-
|
|
290
|
+
// Exposed via the named yield to enable custom hide events
|
|
291
|
+
@action
|
|
292
|
+
hide() {
|
|
293
|
+
this._hide();
|
|
294
|
+
}
|
|
215
295
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
296
|
+
@action
|
|
297
|
+
onParentFinderInsert(element) {
|
|
298
|
+
this.parentElement = element.parentElement;
|
|
299
|
+
this._initializeAttacher();
|
|
300
|
+
}
|
|
219
301
|
|
|
220
|
-
|
|
221
|
-
|
|
302
|
+
@action
|
|
303
|
+
_ensureArgumentsAreValid() {
|
|
304
|
+
stripInProduction(() => {
|
|
305
|
+
if (this.arrow && this.isFillAnimation) {
|
|
306
|
+
warn('Animation: \'fill\' is not compatible with arrow: true', { id: 70015 });
|
|
307
|
+
}
|
|
222
308
|
|
|
223
|
-
|
|
224
|
-
|
|
309
|
+
if (this.useCapture !== this._lastUseCaptureArgumentValue) {
|
|
310
|
+
warn(
|
|
311
|
+
'The value of the useCapture argument was mutated',
|
|
312
|
+
{ id: 'ember-attacher.use-capture-mutated' }
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
225
317
|
|
|
226
|
-
|
|
227
|
-
|
|
318
|
+
|
|
319
|
+
constructor() {
|
|
320
|
+
super(...arguments);
|
|
228
321
|
|
|
229
322
|
// The debounced _hide() and _show() are stored here so they can be cancelled when necessary
|
|
230
323
|
this._delayedVisibilityToggle = null;
|
|
231
324
|
|
|
232
|
-
this.id = this.id || `${guidFor(this)}-popper`;
|
|
233
|
-
|
|
234
325
|
// The final source of truth on whether or not all _hide() or _show() actions have completed
|
|
235
326
|
this._isHidden = true;
|
|
236
327
|
|
|
@@ -257,97 +348,12 @@ export default class AttachPopover extends Component {
|
|
|
257
348
|
this._show = this._show.bind(this);
|
|
258
349
|
this._showAfterDelay = this._showAfterDelay.bind(this);
|
|
259
350
|
|
|
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();
|
|
351
|
+
this._lastUseCaptureArgumentValue = this.useCapture;
|
|
344
352
|
}
|
|
345
353
|
|
|
346
354
|
_initializeAttacher() {
|
|
347
355
|
this._removeEventListeners();
|
|
348
|
-
|
|
349
|
-
this.set('_currentTarget', this.popperTarget || this._parentFinder.parentNode);
|
|
350
|
-
|
|
356
|
+
this._currentTarget = this.args.explicitTarget || this.parentElement;
|
|
351
357
|
this._addListenersForShowEvents();
|
|
352
358
|
|
|
353
359
|
if (!this._isHidden || this.isShown) {
|
|
@@ -360,7 +366,6 @@ export default class AttachPopover extends Component {
|
|
|
360
366
|
}
|
|
361
367
|
|
|
362
368
|
_addListenersForShowEvents() {
|
|
363
|
-
|
|
364
369
|
if (!this._currentTarget) {
|
|
365
370
|
return;
|
|
366
371
|
}
|
|
@@ -372,10 +377,10 @@ export default class AttachPopover extends Component {
|
|
|
372
377
|
});
|
|
373
378
|
}
|
|
374
379
|
|
|
375
|
-
|
|
376
|
-
super.
|
|
380
|
+
willDestroy() {
|
|
381
|
+
super.willDestroy(...arguments);
|
|
377
382
|
|
|
378
|
-
|
|
383
|
+
this._cancelAnimation();
|
|
379
384
|
cancel(this._delayedVisibilityToggle);
|
|
380
385
|
|
|
381
386
|
this._removeEventListeners();
|
|
@@ -386,7 +391,6 @@ export default class AttachPopover extends Component {
|
|
|
386
391
|
document.removeEventListener(eventType, this._hideListenersOnDocumentByEvent[eventType], this.useCapture);
|
|
387
392
|
delete this._hideListenersOnDocumentByEvent[eventType];
|
|
388
393
|
});
|
|
389
|
-
|
|
390
394
|
if (!this._currentTarget) {
|
|
391
395
|
return;
|
|
392
396
|
}
|
|
@@ -399,13 +403,13 @@ export default class AttachPopover extends Component {
|
|
|
399
403
|
});
|
|
400
404
|
}
|
|
401
405
|
|
|
402
|
-
@
|
|
403
|
-
|
|
406
|
+
@action
|
|
407
|
+
onTargetOrTriggerChange() {
|
|
404
408
|
this._initializeAttacher();
|
|
405
409
|
}
|
|
406
410
|
|
|
407
|
-
@
|
|
408
|
-
|
|
411
|
+
@action
|
|
412
|
+
onIsShownChange() {
|
|
409
413
|
const isShown = this.isShown;
|
|
410
414
|
|
|
411
415
|
if (isShown === true && this._isHidden) {
|
|
@@ -426,7 +430,7 @@ export default class AttachPopover extends Component {
|
|
|
426
430
|
_showAfterDelay() {
|
|
427
431
|
cancel(this._delayedVisibilityToggle);
|
|
428
432
|
|
|
429
|
-
this.
|
|
433
|
+
this._mustRender = true;
|
|
430
434
|
|
|
431
435
|
this._addListenersForHideEvents();
|
|
432
436
|
|
|
@@ -436,13 +440,13 @@ export default class AttachPopover extends Component {
|
|
|
436
440
|
}
|
|
437
441
|
|
|
438
442
|
_show() {
|
|
439
|
-
|
|
443
|
+
this._cancelAnimation();
|
|
440
444
|
|
|
441
445
|
if (!this._currentTarget) {
|
|
442
446
|
return;
|
|
443
447
|
}
|
|
444
448
|
|
|
445
|
-
this.
|
|
449
|
+
this._mustRender = true;
|
|
446
450
|
|
|
447
451
|
// Make the attachment visible immediately so transition animations can take place
|
|
448
452
|
this._setIsVisibleAfterDelay(true, 0);
|
|
@@ -457,43 +461,44 @@ export default class AttachPopover extends Component {
|
|
|
457
461
|
// All included animations set opaque: 0, so the attachment is still effectively hidden until
|
|
458
462
|
// the final RAF occurs.
|
|
459
463
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
464
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
460
465
|
if (this.isDestroyed || this.isDestroying || !this._currentTarget) {
|
|
461
466
|
return;
|
|
462
467
|
}
|
|
463
468
|
|
|
464
|
-
const
|
|
469
|
+
const floatingElement = this._floatingElement;
|
|
465
470
|
|
|
466
471
|
// Wait until the element is visible before continuing
|
|
467
|
-
if (!
|
|
468
|
-
this.
|
|
469
|
-
|
|
472
|
+
if (!floatingElement || floatingElement.style.display === 'none') {
|
|
473
|
+
this._startShowAnimation();
|
|
470
474
|
return;
|
|
471
475
|
}
|
|
472
476
|
|
|
473
|
-
this._enableEventListeners();
|
|
474
477
|
this._update();
|
|
475
478
|
|
|
476
479
|
// Wait for the above positioning to take effect before starting the show animation,
|
|
477
480
|
// else the positioning itself will be animated, causing animation glitches.
|
|
478
481
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
482
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
479
483
|
if (this.isDestroyed || this.isDestroying || !this._currentTarget) {
|
|
480
484
|
return;
|
|
481
485
|
}
|
|
482
|
-
|
|
483
486
|
run(() => {
|
|
484
487
|
if (this.isDestroyed || this.isDestroying || !this._currentTarget) {
|
|
485
488
|
return;
|
|
486
489
|
}
|
|
487
|
-
// Make the
|
|
488
|
-
|
|
489
|
-
this.
|
|
490
|
-
this.
|
|
491
|
-
|
|
490
|
+
// Make the floating element visible now that it has been positioned
|
|
491
|
+
floatingElement.style.visibility = '';
|
|
492
|
+
this._transitionDuration = parseInt(this.showDuration);
|
|
493
|
+
this._isStartingAnimation = true;
|
|
494
|
+
floatingElement.setAttribute('aria-hidden', 'false');
|
|
492
495
|
});
|
|
493
496
|
|
|
494
497
|
this._isHidden = false;
|
|
495
498
|
});
|
|
499
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
496
500
|
});
|
|
501
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
497
502
|
}
|
|
498
503
|
|
|
499
504
|
/**
|
|
@@ -509,18 +514,20 @@ export default class AttachPopover extends Component {
|
|
|
509
514
|
}
|
|
510
515
|
|
|
511
516
|
_hide() {
|
|
512
|
-
if (!this.
|
|
517
|
+
if (!this._floatingElement) {
|
|
513
518
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
519
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
514
520
|
this._animationTimeout = this._hide();
|
|
515
521
|
});
|
|
522
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
516
523
|
return;
|
|
517
524
|
}
|
|
518
525
|
|
|
519
|
-
|
|
526
|
+
this._cancelAnimation();
|
|
520
527
|
|
|
521
528
|
this._removeListenersForHideEvents();
|
|
522
|
-
|
|
523
529
|
this._animationTimeout = requestAnimationFrame(() => {
|
|
530
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
524
531
|
// Avoid a race condition where we attempt to hide after the component is being destroyed.
|
|
525
532
|
if (this.isDestroyed || this.isDestroying) {
|
|
526
533
|
return;
|
|
@@ -533,18 +540,16 @@ export default class AttachPopover extends Component {
|
|
|
533
540
|
return;
|
|
534
541
|
}
|
|
535
542
|
|
|
536
|
-
this.
|
|
537
|
-
this.
|
|
538
|
-
this.
|
|
539
|
-
|
|
543
|
+
this._transitionDuration = hideDuration;
|
|
544
|
+
this._isStartingAnimation = false;
|
|
545
|
+
this._floatingElement.setAttribute('aria-hidden', 'true');
|
|
540
546
|
// Wait for any animations to complete before hiding the attachment
|
|
541
547
|
this._setIsVisibleAfterDelay(false, hideDuration);
|
|
542
548
|
});
|
|
543
549
|
|
|
544
|
-
this._disableEventListeners();
|
|
545
|
-
|
|
546
550
|
this._isHidden = true;
|
|
547
551
|
});
|
|
552
|
+
animationTestWaiter.beginAsync(this._animationTimeout);
|
|
548
553
|
}
|
|
549
554
|
|
|
550
555
|
/**
|
|
@@ -628,10 +633,10 @@ export default class AttachPopover extends Component {
|
|
|
628
633
|
return;
|
|
629
634
|
}
|
|
630
635
|
|
|
631
|
-
// If cursor is not on the attachment or target, hide the
|
|
636
|
+
// If cursor is not on the attachment or target, hide the floating element
|
|
632
637
|
if (!target.contains(event.target)
|
|
633
638
|
&& !(this.isOffset && this._isCursorBetweenTargetAndAttachment(event))
|
|
634
|
-
&& (this.
|
|
639
|
+
&& (this._floatingElement && !this._floatingElement.contains(event.target))) {
|
|
635
640
|
// Remove this listener before hiding the attachment
|
|
636
641
|
delete this._hideListenersOnDocumentByEvent.mousemove;
|
|
637
642
|
document.removeEventListener('mousemove', this._hideIfMouseOutsideTargetOrAttachment, this.useCapture);
|
|
@@ -648,7 +653,7 @@ export default class AttachPopover extends Component {
|
|
|
648
653
|
|
|
649
654
|
const { clientX, clientY } = event;
|
|
650
655
|
|
|
651
|
-
const attachmentPosition = this.
|
|
656
|
+
const attachmentPosition = this._floatingElement.getBoundingClientRect();
|
|
652
657
|
const targetPosition = this._currentTarget.getBoundingClientRect();
|
|
653
658
|
|
|
654
659
|
const isBetweenLeftAndRight = clientX > Math.min(attachmentPosition.left, targetPosition.left)
|
|
@@ -692,7 +697,7 @@ export default class AttachPopover extends Component {
|
|
|
692
697
|
const targetReceivedClick = this._currentTarget.contains(event.target);
|
|
693
698
|
|
|
694
699
|
if (this.interactive) {
|
|
695
|
-
if (!targetReceivedClick && !this.
|
|
700
|
+
if (!targetReceivedClick && !this._floatingElement.contains(event.target)) {
|
|
696
701
|
this._hideAfterDelay();
|
|
697
702
|
}
|
|
698
703
|
} else if (!targetReceivedClick) {
|
|
@@ -718,7 +723,7 @@ export default class AttachPopover extends Component {
|
|
|
718
723
|
const targetContainsFocus = this._currentTarget.contains(event.relatedTarget);
|
|
719
724
|
|
|
720
725
|
if (this.interactive) {
|
|
721
|
-
if (!targetContainsFocus && !this.
|
|
726
|
+
if (!targetContainsFocus && !this._floatingElement.contains(event.relatedTarget)) {
|
|
722
727
|
this._hideAfterDelay();
|
|
723
728
|
}
|
|
724
729
|
} else if (!targetContainsFocus) {
|
|
@@ -762,4 +767,75 @@ export default class AttachPopover extends Component {
|
|
|
762
767
|
}
|
|
763
768
|
});
|
|
764
769
|
}
|
|
770
|
+
|
|
771
|
+
@action
|
|
772
|
+
didInsertFloatingElement(floatingElement) {
|
|
773
|
+
this._floatingElement = floatingElement;
|
|
774
|
+
|
|
775
|
+
if (this.renderInPlace) {
|
|
776
|
+
this.parentElement = floatingElement.parentElement;
|
|
777
|
+
this._initializeAttacher();
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
@action
|
|
782
|
+
didInsertArrow(element) {
|
|
783
|
+
this._arrowElement = element;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
@action
|
|
787
|
+
onOptionsChange() {
|
|
788
|
+
this._ensureArgumentsAreValid();
|
|
789
|
+
this._update();
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
@action
|
|
793
|
+
willDestroyFloatingElement() {
|
|
794
|
+
this._cleanup?.();
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
_update() {
|
|
798
|
+
this._cleanup?.();
|
|
799
|
+
if (this.autoUpdate) {
|
|
800
|
+
this._cleanup = autoUpdate(
|
|
801
|
+
this._currentTarget,
|
|
802
|
+
this._floatingElement,
|
|
803
|
+
this._updatePosition,
|
|
804
|
+
typeOf(this.autoUpdate) === 'object' ? this.autoUpdate : undefined
|
|
805
|
+
);
|
|
806
|
+
} else {
|
|
807
|
+
this._updatePosition();
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
@action
|
|
812
|
+
_updatePosition() {
|
|
813
|
+
const computePositionToken = animationTestWaiter.beginAsync();
|
|
814
|
+
computePosition(this._currentTarget, this._floatingElement, {
|
|
815
|
+
...this.floatingUiOptions,
|
|
816
|
+
middleware: this._middleware,
|
|
817
|
+
placement: this.placement
|
|
818
|
+
}).then(({ x, y, placement, middlewareData }) => {
|
|
819
|
+
animationTestWaiter.endAsync(computePositionToken);
|
|
820
|
+
Object.assign(this._floatingElement.style, { left: `${x}px`, top: `${y}px`, });
|
|
821
|
+
|
|
822
|
+
if (middlewareData.arrow) {
|
|
823
|
+
const { x, y } = middlewareData.arrow;
|
|
824
|
+
|
|
825
|
+
Object.assign(this._arrowElement.style, {
|
|
826
|
+
left: x != null ? `${x}px` : '',
|
|
827
|
+
top: y != null ? `${y}px` : '',
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
this._floatingElement.setAttribute('x-placement', placement);
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
_cancelAnimation() {
|
|
835
|
+
cancelAnimationFrame(this._animationTimeout);
|
|
836
|
+
|
|
837
|
+
if (animationTestWaiter.items.get(this._animationTimeout)) {
|
|
838
|
+
animationTestWaiter.endAsync(this._animationTimeout);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
765
841
|
}
|