mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-23 09:33:01 +03:00
implement a tooltip which always floats correctly
This commit is contained in:
parent
af8cd41476
commit
f948f0d24f
@ -82,6 +82,7 @@
|
||||
"storybook": "^7.0.0-rc.8",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-check": "^3.0.1",
|
||||
"svelte-floating-ui": "^1.5.2",
|
||||
"tailwindcss": "^3.1.5",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.8.4",
|
||||
|
@ -63,6 +63,7 @@ specifiers:
|
||||
storybook: ^7.0.0-rc.8
|
||||
svelte: ^3.55.1
|
||||
svelte-check: ^3.0.1
|
||||
svelte-floating-ui: ^1.5.2
|
||||
svelte-french-toast: ^1.0.3
|
||||
tailwindcss: ^3.1.5
|
||||
tauri-plugin-log-api: github:tauri-apps/tauri-plugin-log
|
||||
@ -139,6 +140,7 @@ devDependencies:
|
||||
storybook: 7.0.0-rc.8
|
||||
svelte: 3.55.1
|
||||
svelte-check: 3.0.3_gqx7lw3sljhsd4bstor5m2aa2u
|
||||
svelte-floating-ui: 1.5.2
|
||||
tailwindcss: 3.2.4_postcss@8.4.21
|
||||
tslib: 2.5.0
|
||||
typescript: 4.9.5
|
||||
@ -2029,6 +2031,16 @@ packages:
|
||||
resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==}
|
||||
dev: true
|
||||
|
||||
/@floating-ui/core/1.2.5:
|
||||
resolution: {integrity: sha512-qrcbyfnRVziRlB6IYwjCopYhO7Vud750JlJyuljruIXcPxr22y8zdckcJGsuOdnQ639uVD1tTXddrcH3t3QYIQ==}
|
||||
dev: true
|
||||
|
||||
/@floating-ui/dom/1.2.5:
|
||||
resolution: {integrity: sha512-+sAUfpQ3Frz+VCbPCqj+cZzvEESy3fjSeT/pDWkYCWOBXYNNKZfuVsHuv8/JO2zze8+Eb/Q7a6hZVgzS81fLbQ==}
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.2.5
|
||||
dev: true
|
||||
|
||||
/@humanwhocodes/config-array/0.11.8:
|
||||
resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
@ -2759,6 +2771,17 @@ packages:
|
||||
telejson: 7.0.4
|
||||
dev: true
|
||||
|
||||
/@storybook/channel-postmessage/7.0.0-rc.9:
|
||||
resolution: {integrity: sha512-iaEwRN4PvIzxzH8dGXKSKVkepC2+70O6MM4Z+zDOyN1Q6p4grYOddM2Olqdb9cf3TaYES5JENPe5ig62JLu2zg==}
|
||||
dependencies:
|
||||
'@storybook/channels': 7.0.0-rc.9
|
||||
'@storybook/client-logger': 7.0.0-rc.9
|
||||
'@storybook/core-events': 7.0.0-rc.9
|
||||
'@storybook/global': 5.0.0
|
||||
qs: 6.11.1
|
||||
telejson: 7.0.4
|
||||
dev: true
|
||||
|
||||
/@storybook/channel-websocket/7.0.0-rc.8:
|
||||
resolution: {integrity: sha512-BcWlji7W98+pmn8Xhk7mC/oKVljipyOIqxzJFPDgLE9GFRshwoL3j5qBVhHS5aSGFyqNi8VhBp5FztFgsbLNJA==}
|
||||
dependencies:
|
||||
@ -2772,6 +2795,10 @@ packages:
|
||||
resolution: {integrity: sha512-2tI/ECbQcXjncYGLVdrttNT8adIp6kV/bnQGJWmF5hBXZ7Izwyq1WRPTgPT++RihmOOTHvkRx4GCKfwluOrNpA==}
|
||||
dev: true
|
||||
|
||||
/@storybook/channels/7.0.0-rc.9:
|
||||
resolution: {integrity: sha512-XqgHzEqAlNG9lfNjYTLepXQNafoD7cUSDSFbZkpOAUlYRsBz0cyhCGyxAWDyH7er8ZJCCIfxsEwZVaMGg9bLFQ==}
|
||||
dev: true
|
||||
|
||||
/@storybook/cli/7.0.0-rc.8:
|
||||
resolution: {integrity: sha512-RUiiHCUEHPYMlw2Li3ubVKwy6aKvHPlrWvUhUcAtWK+iWo8oia+3A/vCo4aCQvpOIuhAIICz6ipg9vhm1Bw7zQ==}
|
||||
hasBin: true
|
||||
@ -2827,6 +2854,12 @@ packages:
|
||||
'@storybook/global': 5.0.0
|
||||
dev: true
|
||||
|
||||
/@storybook/client-logger/7.0.0-rc.9:
|
||||
resolution: {integrity: sha512-ihBDLgfECGXaP54KXMcTLokOXdKryoKSF6UEErJpzb/Foq8g/OQSaklSjx0xdoEt01JT+cJEdQOLAewNK/4fYg==}
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
dev: true
|
||||
|
||||
/@storybook/codemod/7.0.0-rc.8:
|
||||
resolution: {integrity: sha512-3WtdAHlcWiOZhdq5JhQ+fYQbOglkg6IS5nd9ns4It/u/l7b6Zt3EpLwtqduav/QbaTngo4jzpV/97eW9VKXXXg==}
|
||||
dependencies:
|
||||
@ -2903,6 +2936,10 @@ packages:
|
||||
resolution: {integrity: sha512-KsKf+Ob6zQ8+IJ9oDD5xqASwYGzcjT08azBjSt4yocHIJ3mY741h88YDS0wcwnM+JrV6iFYlY0hiK35lnBEddA==}
|
||||
dev: true
|
||||
|
||||
/@storybook/core-events/7.0.0-rc.9:
|
||||
resolution: {integrity: sha512-FDEDaaksKJFGeaME1lnxwQr7AQxBFZoPJD8jX9t5hniFtouJ7Pdn8lGDueJME2XNuklT4VZHAUwFVO3WcbxbzA==}
|
||||
dev: true
|
||||
|
||||
/@storybook/core-server/7.0.0-rc.8:
|
||||
resolution: {integrity: sha512-IpA5RY+xRG/gfzPoJMO3PBgZgu0bgevta8KGskQ01ljCP5JNRfbU3fEdY28Dai/xq1Jaj0WR1xyGHuCZgi5D+w==}
|
||||
dependencies:
|
||||
@ -3019,6 +3056,16 @@ packages:
|
||||
'@storybook/preview-api': 7.0.0-rc.8
|
||||
dev: true
|
||||
|
||||
/@storybook/instrumenter/7.0.0-rc.9:
|
||||
resolution: {integrity: sha512-Uyq4ZjuEcG7ThZqq4L0YsGX5LnbP85yi25k+1ySkr677eft0VpTp5U37jmyChGkoI03kghVNvM16LTgVuQ83kw==}
|
||||
dependencies:
|
||||
'@storybook/channels': 7.0.0-rc.9
|
||||
'@storybook/client-logger': 7.0.0-rc.9
|
||||
'@storybook/core-events': 7.0.0-rc.9
|
||||
'@storybook/global': 5.0.0
|
||||
'@storybook/preview-api': 7.0.0-rc.9
|
||||
dev: true
|
||||
|
||||
/@storybook/manager-api/7.0.0-rc.8_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-PV3N2tvUp6BTehgKt7JwwYH+ALoe7k2iKMeMKPNGwoRvbESmJfFEdAK95DyIZoLm8LUUtX1FpzcUek/7YWJ17w==}
|
||||
peerDependencies:
|
||||
@ -3086,6 +3133,26 @@ packages:
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/@storybook/preview-api/7.0.0-rc.9:
|
||||
resolution: {integrity: sha512-KcbqKm4Ge0G8iZNzCnIzGKY7ZD4L6140BQexid0t6dDvBRGavkUXvg3IH+nzXFTHW4H9lW3yFY3zbPANjUexXw==}
|
||||
dependencies:
|
||||
'@storybook/channel-postmessage': 7.0.0-rc.9
|
||||
'@storybook/channels': 7.0.0-rc.9
|
||||
'@storybook/client-logger': 7.0.0-rc.9
|
||||
'@storybook/core-events': 7.0.0-rc.9
|
||||
'@storybook/csf': 0.0.2-next.10
|
||||
'@storybook/global': 5.0.0
|
||||
'@storybook/types': 7.0.0-rc.9
|
||||
'@types/qs': 6.9.7
|
||||
dequal: 2.0.3
|
||||
lodash: 4.17.21
|
||||
memoizerific: 1.11.3
|
||||
qs: 6.11.1
|
||||
synchronous-promise: 2.0.17
|
||||
ts-dedent: 2.2.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/@storybook/preview/7.0.0-rc.8:
|
||||
resolution: {integrity: sha512-ZWf/0MXR+6V19fXcNE9Sy0DCRuR1+P8S4Ai7FSy9iUhckk7QTmtgzV+Ob+2NIltZqWjbtDXHquBB6fcJqXqNmg==}
|
||||
dev: true
|
||||
@ -3201,8 +3268,8 @@ packages:
|
||||
/@storybook/testing-library/0.0.14-next.1:
|
||||
resolution: {integrity: sha512-1CAl40IKIhcPaCC4pYCG0b9IiYNymktfV/jTrX7ctquRY3akaN7f4A1SippVHosksft0M+rQTFE0ccfWW581fw==}
|
||||
dependencies:
|
||||
'@storybook/client-logger': 7.0.0-rc.8
|
||||
'@storybook/instrumenter': 7.0.0-rc.8
|
||||
'@storybook/client-logger': 7.0.0-rc.9
|
||||
'@storybook/instrumenter': 7.0.0-rc.9
|
||||
'@testing-library/dom': 8.20.0
|
||||
'@testing-library/user-event': 13.5.0_yxlyej73nftwmh2fiao7paxmlm
|
||||
ts-dedent: 2.2.0
|
||||
@ -3231,6 +3298,15 @@ packages:
|
||||
file-system-cache: 2.0.2
|
||||
dev: true
|
||||
|
||||
/@storybook/types/7.0.0-rc.9:
|
||||
resolution: {integrity: sha512-fnlEM+p1h8KAESKa0g+JK6FC5pwS6rci+vG1TexzMWPyk87bJeqZb7aFhhhd5t7UK5CQnZjka9t8RXD7atXk7A==}
|
||||
dependencies:
|
||||
'@storybook/channels': 7.0.0-rc.9
|
||||
'@types/babel__core': 7.20.0
|
||||
'@types/express': 4.17.17
|
||||
file-system-cache: 2.0.2
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-static/1.0.0-next.50_l5ueyfihz3gpzzvvyo2ean5u3e:
|
||||
resolution: {integrity: sha512-xZKBmiwFGW8nrH8+eysUAAo9XrtApI81q0m67y1bexVw8IY7/x741b6VEklNM7BZ7js0Mi2x+yCkHpOee8UZKQ==}
|
||||
peerDependencies:
|
||||
@ -7862,6 +7938,13 @@ packages:
|
||||
- sugarss
|
||||
dev: true
|
||||
|
||||
/svelte-floating-ui/1.5.2:
|
||||
resolution: {integrity: sha512-nV50eno74CEsFfFJ6iyN/oNYDEOck1TZjGV3lmJksVRbiiUAVF6bHspyAhR7GZ7c/4qbRWp9UyX24J+UXdEpag==}
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.2.5
|
||||
'@floating-ui/dom': 1.2.5
|
||||
dev: true
|
||||
|
||||
/svelte-french-toast/1.0.3_svelte@3.55.1:
|
||||
resolution: {integrity: sha512-HBdutlqUx5FroucvcmMJstmTJDedXayiOSXubBYW6iL5x1PdHp+bOflGx5NxsOa6+EQckJnnVv1vJYgGsg7ngQ==}
|
||||
dependencies:
|
||||
|
72
src/lib/components/Tooltip.svelte
Normal file
72
src/lib/components/Tooltip.svelte
Normal file
@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom';
|
||||
import { arrow } from 'svelte-floating-ui';
|
||||
import { createFloatingActions } from 'svelte-floating-ui';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
let arrowRef = writable({} as HTMLElement);
|
||||
|
||||
export let label: string;
|
||||
|
||||
const [floatingRef, floatingContent, update] = createFloatingActions({
|
||||
strategy: 'absolute',
|
||||
placement: 'bottom',
|
||||
middleware: [offset(6), flip(), shift(), arrow({ element: arrowRef })],
|
||||
onComputed({ placement, middlewareData }) {
|
||||
if (!middlewareData.arrow) return;
|
||||
const { x, y } = middlewareData.arrow;
|
||||
const staticSide = {
|
||||
top: 'bottom',
|
||||
right: 'left',
|
||||
bottom: 'top',
|
||||
left: 'right'
|
||||
}[placement.split('-')[0]] as any;
|
||||
|
||||
if (!$arrowRef) return;
|
||||
Object.assign($arrowRef.style, {
|
||||
left: x != null ? `${x}px` : '',
|
||||
top: y != null ? `${y}px` : '',
|
||||
[staticSide]: '-4px'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let showTooltip = false;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="h-fit w-fit"
|
||||
on:mouseenter={() => (showTooltip = true)}
|
||||
on:mouseleave={() => (showTooltip = false)}
|
||||
use:floatingRef
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
{#if showTooltip}
|
||||
<div
|
||||
role="tooltip"
|
||||
class="
|
||||
absolute
|
||||
rounded-[5px]
|
||||
bg-[#171717]
|
||||
p-2
|
||||
text-base
|
||||
text-[#FAFAFA]
|
||||
shadow-lg
|
||||
"
|
||||
use:floatingContent
|
||||
>
|
||||
{label}
|
||||
<div
|
||||
class="
|
||||
absolute
|
||||
h-3
|
||||
w-3 rotate-45
|
||||
rounded-sm
|
||||
bg-[#171717]
|
||||
"
|
||||
bind:this={$arrowRef}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
@ -7,3 +7,4 @@ export { default as Modal } from './Modal.svelte';
|
||||
export { default as Button } from './Button.svelte';
|
||||
export { default as ButtonGroup } from './ButtonGroup.svelte';
|
||||
export { default as Dialog } from './Dialog.svelte';
|
||||
export { default as Tooltip } from './Tooltip.svelte';
|
||||
|
22
src/stories/Tooltip.stories.ts
Normal file
22
src/stories/Tooltip.stories.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import type { Meta, StoryObj } from '@storybook/svelte';
|
||||
|
||||
import TooltipOnText from './TooltipOnText.svelte';
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/7.0/svelte/writing-stories/introduction
|
||||
const meta: Meta<TooltipOnText> = {
|
||||
title: 'GitButler/Tooltip',
|
||||
component: TooltipOnText,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
label: { control: 'text' }
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<TooltipOnText>;
|
||||
|
||||
export const TextWithTooltip: Story = {
|
||||
args: {
|
||||
label: 'This is a tooltip'
|
||||
}
|
||||
};
|
18
src/stories/TooltipOnText.svelte
Normal file
18
src/stories/TooltipOnText.svelte
Normal file
@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
|
||||
export let label: string;
|
||||
</script>
|
||||
|
||||
<!--
|
||||
@component
|
||||
This exists only to facilitate testing of the `Tooltip` component in Storybook.
|
||||
Do not use this in the app.
|
||||
Do not style this.
|
||||
Do not add any logic to this.
|
||||
-->
|
||||
<div class="bg-[#737373] p-16">
|
||||
<Tooltip {label}>
|
||||
<span>Hover me!</span>
|
||||
</Tooltip>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user