Skip to content

Commit 46d9f94

Browse files
committed
move to floating-ui
1 parent 2b4854d commit 46d9f94

File tree

4 files changed

+145
-85
lines changed

4 files changed

+145
-85
lines changed

package-lock.json

+45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"webpack-dev-server": "^4.11.1"
9797
},
9898
"dependencies": {
99+
"@floating-ui/dom": "^1.5.1",
99100
"@popperjs/core": "^2.11.8"
100101
},
101102
"browserslist": "last 2 versions"

src/Popover.svelte

+45-44
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
<script>
2+
import {
3+
autoUpdate,
4+
computePosition,
5+
flip,
6+
limitShift,
7+
offset,
8+
shift
9+
} from '@floating-ui/dom';
210
import { onMount } from 'svelte';
3-
import { createPopper } from '@popperjs/core';
411
import classnames from './utils';
512
import InlineContainer from './InlineContainer.svelte';
613
import Portal from './Portal.svelte';
@@ -16,40 +23,34 @@
1623
export let target = '';
1724
export let title = '';
1825
export let trigger = 'click';
19-
let targetEl;
20-
let popoverEl;
21-
let popperInstance;
26+
let referenceEl;
27+
let floatingEl;
2228
let bsPlacement;
2329
let popperPlacement = placement;
24-
25-
const checkPopperPlacement = {
26-
name: 'checkPopperPlacement',
27-
enabled: true,
28-
phase: 'main',
29-
fn({ state }) {
30-
popperPlacement = state.placement;
31-
}
32-
};
30+
let cleanup;
3331
3432
$: {
35-
if (isOpen && popoverEl) {
36-
popperInstance = createPopper(targetEl, popoverEl, {
37-
placement,
38-
modifiers: [
39-
checkPopperPlacement,
40-
{
41-
name: 'offset',
42-
options: {
43-
offset: () => {
44-
return [0, 8];
45-
}
46-
}
47-
}
48-
]
33+
if (isOpen && floatingEl) {
34+
cleanup = autoUpdate(referenceEl, floatingEl, () => {
35+
floatingEl.style = {
36+
width: 'max-content',
37+
position: 'absolute',
38+
top: 0,
39+
left: 0
40+
};
41+
computePosition(referenceEl, floatingEl, {
42+
placement: popperPlacement,
43+
middleware: [offset(8), flip(), shift({ limiter: limitShift() })],
44+
}).then(({x, y}) => {
45+
Object.assign(floatingEl.style, {
46+
left: `${x}px`,
47+
top: `${y}px`,
48+
});
49+
})
4950
});
50-
} else if (popperInstance) {
51-
popperInstance.destroy();
52-
popperInstance = undefined;
51+
} else if (cleanup) {
52+
cleanup();
53+
cleanup = undefined;
5354
}
5455
}
5556
@@ -58,34 +59,34 @@
5859
const toggle = () => (isOpen = !isOpen);
5960
6061
onMount(() => {
61-
targetEl = document.querySelector(`#${target}`);
62+
referenceEl = document.querySelector(`#${target}`);
6263
switch (trigger) {
6364
case 'hover':
64-
targetEl.addEventListener('mouseover', open);
65-
targetEl.addEventListener('mouseleave', close);
65+
referenceEl.addEventListener('mouseover', open);
66+
referenceEl.addEventListener('mouseleave', close);
6667
break;
6768
case 'focus':
68-
targetEl.addEventListener('focus', open);
69-
targetEl.addEventListener('blur', close);
69+
referenceEl.addEventListener('focus', open);
70+
referenceEl.addEventListener('blur', close);
7071
break;
7172
default:
72-
targetEl.addEventListener('click', toggle);
73-
if (dismissible) targetEl.addEventListener('blur', close);
73+
referenceEl.addEventListener('click', toggle);
74+
if (dismissible) referenceEl.addEventListener('blur', close);
7475
break;
7576
}
7677
return () => {
7778
switch (trigger) {
7879
case 'hover':
79-
targetEl.removeEventListener('mouseover', open);
80-
targetEl.removeEventListener('mouseleave', close);
80+
referenceEl.removeEventListener('mouseover', open);
81+
referenceEl.removeEventListener('mouseleave', close);
8182
break;
8283
case 'focus':
83-
targetEl.removeEventListener('focus', open);
84-
targetEl.removeEventListener('blur', close);
84+
referenceEl.removeEventListener('focus', open);
85+
referenceEl.removeEventListener('blur', close);
8586
break;
8687
default:
87-
targetEl.removeEventListener('click', toggle);
88-
if (dismissible) targetEl.removeEventListener('blur', close);
88+
referenceEl.removeEventListener('click', toggle);
89+
if (dismissible) referenceEl.removeEventListener('blur', close);
8990
break;
9091
}
9192
};
@@ -115,7 +116,7 @@
115116
{#if isOpen}
116117
<svelte:component this={outer}>
117118
<div
118-
bind:this={popoverEl}
119+
bind:this={floatingEl}
119120
{...$$restProps}
120121
class={classes}
121122
role="tooltip"

src/Tooltip.svelte

+54-41
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<script>
2+
import {
3+
autoUpdate,
4+
computePosition,
5+
flip,
6+
limitShift,
7+
shift
8+
} from '@floating-ui/dom';
29
import { onDestroy, onMount } from 'svelte';
3-
import { createPopper } from '@popperjs/core/dist/esm/popper';
410
import classnames, { uuid } from './utils';
511
import InlineContainer from './InlineContainer.svelte';
612
import Portal from './Portal.svelte';
@@ -15,29 +21,27 @@
1521
export let placement = 'top';
1622
export let target = '';
1723
let bsPlacement;
18-
let popperInstance;
1924
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;
3128
3229
$: {
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+
})
3741
});
38-
} else if (popperInstance) {
39-
popperInstance.destroy();
40-
popperInstance = undefined;
42+
} else if (cleanup) {
43+
cleanup();
44+
cleanup = undefined;
4145
}
4246
}
4347
@@ -54,51 +58,51 @@
5458
5559
function registerEventListeners() {
5660
if (target == null || target.length == 0) {
57-
targetEl = null;
61+
referenceEl = null;
5862
return;
5963
}
6064
6165
// Check if target is HTMLElement
6266
try {
6367
if (target instanceof HTMLElement) {
64-
targetEl = target;
68+
referenceEl = target;
6569
}
6670
} catch (e) {
6771
// fails on SSR
6872
}
6973
70-
// If targetEl has not been found yet
71-
if (targetEl == null) {
74+
// If referenceEl has not been found yet
75+
if (referenceEl == null) {
7276
// Check if target can be found via querySelector
7377
try {
74-
targetEl = document.querySelector(`#${target}`);
78+
referenceEl = document.querySelector(`#${target}`);
7579
} catch (e) {
7680
// fails on SSR
7781
}
7882
}
7983
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);
8690
}
8791
}
8892
8993
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');
96100
}
97101
}
98102
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');
102106
}
103107
104108
$: {
@@ -121,7 +125,7 @@
121125
{#if isOpen}
122126
<svelte:component this={outer}>
123127
<div
124-
bind:this={tooltipEl}
128+
bind:this={floatingEl}
125129
{...$$restProps}
126130
class={classes}
127131
{id}
@@ -139,3 +143,12 @@
139143
</div>
140144
</svelte:component>
141145
{/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

Comments
 (0)