|
1 | 1 | <script>
|
| 2 | + import { |
| 3 | + autoUpdate, |
| 4 | + computePosition, |
| 5 | + flip, |
| 6 | + limitShift, |
| 7 | + shift |
| 8 | + } from '@floating-ui/dom'; |
2 | 9 | import { onDestroy, onMount } from 'svelte';
|
3 |
| - import { createPopper } from '@popperjs/core/dist/esm/popper'; |
4 | 10 | import classnames, { uuid } from './utils';
|
5 | 11 | import InlineContainer from './InlineContainer.svelte';
|
6 | 12 | import Portal from './Portal.svelte';
|
|
15 | 21 | export let placement = 'top';
|
16 | 22 | export let target = '';
|
17 | 23 | let bsPlacement;
|
18 |
| - let popperInstance; |
19 | 24 | let popperPlacement = placement;
|
20 |
| - let targetEl; |
21 |
| - let tooltipEl; |
22 |
| -
|
23 |
| - const checkPopperPlacement = { |
24 |
| - name: 'checkPopperPlacement', |
25 |
| - enabled: true, |
26 |
| - phase: 'main', |
27 |
| - fn({ state }) { |
28 |
| - popperPlacement = state.placement; |
29 |
| - } |
30 |
| - }; |
| 25 | + let referenceEl; |
| 26 | + let floatingEl; |
| 27 | + let cleanup; |
31 | 28 |
|
32 | 29 | $: {
|
33 |
| - if (isOpen && tooltipEl) { |
34 |
| - popperInstance = createPopper(targetEl, tooltipEl, { |
35 |
| - placement, |
36 |
| - modifiers: [checkPopperPlacement] |
| 30 | + if (isOpen && floatingEl) { |
| 31 | + cleanup = autoUpdate(referenceEl, floatingEl, () => { |
| 32 | + computePosition(referenceEl, floatingEl, { |
| 33 | + placement: popperPlacement, |
| 34 | + middleware: [flip(), shift({ limiter: limitShift() })], |
| 35 | + }).then(({x, y}) => { |
| 36 | + Object.assign(floatingEl.style, { |
| 37 | + left: `${x}px`, |
| 38 | + top: `${y}px`, |
| 39 | + }); |
| 40 | + }) |
37 | 41 | });
|
38 |
| - } else if (popperInstance) { |
39 |
| - popperInstance.destroy(); |
40 |
| - popperInstance = undefined; |
| 42 | + } else if (cleanup) { |
| 43 | + cleanup(); |
| 44 | + cleanup = undefined; |
41 | 45 | }
|
42 | 46 | }
|
43 | 47 |
|
|
54 | 58 |
|
55 | 59 | function registerEventListeners() {
|
56 | 60 | if (target == null || target.length == 0) {
|
57 |
| - targetEl = null; |
| 61 | + referenceEl = null; |
58 | 62 | return;
|
59 | 63 | }
|
60 | 64 |
|
61 | 65 | // Check if target is HTMLElement
|
62 | 66 | try {
|
63 | 67 | if (target instanceof HTMLElement) {
|
64 |
| - targetEl = target; |
| 68 | + referenceEl = target; |
65 | 69 | }
|
66 | 70 | } catch (e) {
|
67 | 71 | // fails on SSR
|
68 | 72 | }
|
69 | 73 |
|
70 |
| - // If targetEl has not been found yet |
71 |
| - if (targetEl == null) { |
| 74 | + // If referenceEl has not been found yet |
| 75 | + if (referenceEl == null) { |
72 | 76 | // Check if target can be found via querySelector
|
73 | 77 | try {
|
74 |
| - targetEl = document.querySelector(`#${target}`); |
| 78 | + referenceEl = document.querySelector(`#${target}`); |
75 | 79 | } catch (e) {
|
76 | 80 | // fails on SSR
|
77 | 81 | }
|
78 | 82 | }
|
79 | 83 |
|
80 |
| - // If we've found targetEl |
81 |
| - if (targetEl) { |
82 |
| - targetEl.addEventListener('mouseover', open); |
83 |
| - targetEl.addEventListener('mouseleave', close); |
84 |
| - targetEl.addEventListener('focus', open); |
85 |
| - targetEl.addEventListener('blur', close); |
| 84 | + // If we've found referenceEl |
| 85 | + if (referenceEl) { |
| 86 | + referenceEl.addEventListener('mouseover', open); |
| 87 | + referenceEl.addEventListener('mouseleave', close); |
| 88 | + referenceEl.addEventListener('focus', open); |
| 89 | + referenceEl.addEventListener('blur', close); |
86 | 90 | }
|
87 | 91 | }
|
88 | 92 |
|
89 | 93 | function unregisterEventListeners() {
|
90 |
| - if (targetEl) { |
91 |
| - targetEl.removeEventListener('mouseover', open); |
92 |
| - targetEl.removeEventListener('mouseleave', close); |
93 |
| - targetEl.removeEventListener('focus', open); |
94 |
| - targetEl.removeEventListener('blur', close); |
95 |
| - targetEl.removeAttribute('aria-describedby'); |
| 94 | + if (referenceEl) { |
| 95 | + referenceEl.removeEventListener('mouseover', open); |
| 96 | + referenceEl.removeEventListener('mouseleave', close); |
| 97 | + referenceEl.removeEventListener('focus', open); |
| 98 | + referenceEl.removeEventListener('blur', close); |
| 99 | + referenceEl.removeAttribute('aria-describedby'); |
96 | 100 | }
|
97 | 101 | }
|
98 | 102 |
|
99 |
| - $: if (targetEl) { |
100 |
| - if (isOpen) targetEl.setAttribute('aria-describedby', id); |
101 |
| - else targetEl.removeAttribute('aria-describedby'); |
| 103 | + $: if (referenceEl) { |
| 104 | + if (isOpen) referenceEl.setAttribute('aria-describedby', id); |
| 105 | + else referenceEl.removeAttribute('aria-describedby'); |
102 | 106 | }
|
103 | 107 |
|
104 | 108 | $: {
|
|
121 | 125 | {#if isOpen}
|
122 | 126 | <svelte:component this={outer}>
|
123 | 127 | <div
|
124 |
| - bind:this={tooltipEl} |
| 128 | + bind:this={floatingEl} |
125 | 129 | {...$$restProps}
|
126 | 130 | class={classes}
|
127 | 131 | {id}
|
|
139 | 143 | </div>
|
140 | 144 | </svelte:component>
|
141 | 145 | {/if}
|
| 146 | + |
| 147 | +<style> |
| 148 | + :global(.floating) { |
| 149 | + width: max-content; |
| 150 | + position: absolute; |
| 151 | + top: 0; |
| 152 | + left: 0; |
| 153 | + } |
| 154 | +</style> |
0 commit comments