|
3 | 3 |
|
4 | 4 | import type { IconName } from '$lib/holocene/icon'; |
5 | 5 | import Icon from '$lib/holocene/icon/icon.svelte'; |
| 6 | + import Portal from '$lib/holocene/portal/portal.svelte'; |
| 7 | + import type { PortalPosition } from '$lib/holocene/portal/types'; |
6 | 8 | import type { Only } from '$lib/types/global'; |
7 | 9 |
|
8 | 10 | type BaseProps = { |
|
13 | 15 | class?: string; |
14 | 16 | tooltipClass?: string; |
15 | 17 | show?: boolean; |
| 18 | + usePortal?: boolean; |
| 19 | + scrollContainer?: string; |
16 | 20 | }; |
17 | 21 |
|
18 | 22 | type BasePositionProps = { |
|
71 | 75 | export let width: number | null = null; |
72 | 76 | export let tooltipClass = ''; |
73 | 77 | export let show = false; |
| 78 | + export let usePortal = false; |
| 79 | + export let scrollContainer: string | undefined = undefined; |
| 80 | +
|
| 81 | + let wrapperElement: HTMLElement | null = null; |
| 82 | + let isHovered = false; |
| 83 | +
|
| 84 | + $: portalPosition = ((): PortalPosition => { |
| 85 | + if (top) return 'top'; |
| 86 | + if (topRight) return 'top-right'; |
| 87 | + if (right) return 'right'; |
| 88 | + if (bottomRight) return 'bottom-right'; |
| 89 | + if (bottom) return 'bottom'; |
| 90 | + if (bottomLeft) return 'bottom-left'; |
| 91 | + if (left) return 'left'; |
| 92 | + if (topLeft) return 'top-left'; |
| 93 | + return 'top'; |
| 94 | + })(); |
74 | 95 | </script> |
75 | 96 |
|
76 | 97 | {#if hide} |
77 | 98 | <slot /> |
78 | 99 | {:else} |
79 | | - <div class={merge('wrapper group relative inline-block', className)}> |
| 100 | + <!-- svelte-ignore a11y-no-static-element-interactions --> |
| 101 | + <div |
| 102 | + bind:this={wrapperElement} |
| 103 | + class={merge('wrapper group relative inline-block', className)} |
| 104 | + on:mouseenter={() => (isHovered = true)} |
| 105 | + on:mouseleave={() => (isHovered = false)} |
| 106 | + > |
80 | 107 | <slot /> |
81 | | - <div |
82 | | - class={merge( |
83 | | - 'tooltip absolute left-0 top-0 z-50 hidden translate-x-12 whitespace-nowrap text-xs opacity-0 transition-all group-hover:inline-block group-hover:opacity-95', |
84 | | - show && 'inline-block opacity-95', |
85 | | - )} |
86 | | - class:left |
87 | | - class:right |
88 | | - class:bottom |
89 | | - class:bottomLeft |
90 | | - class:bottomRight |
91 | | - class:top |
92 | | - class:topRight |
93 | | - class:topLeft |
94 | | - style={width ? `white-space: pre-wrap; width: ${width}px;` : null} |
95 | | - > |
| 108 | + |
| 109 | + {#if usePortal && wrapperElement} |
| 110 | + <Portal |
| 111 | + anchor={wrapperElement} |
| 112 | + open={show || isHovered} |
| 113 | + position={portalPosition} |
| 114 | + {scrollContainer} |
| 115 | + > |
| 116 | + <div |
| 117 | + class={merge( |
| 118 | + 'inline-block rounded-md bg-slate-800 px-2 py-2 text-xs text-slate-50', |
| 119 | + tooltipClass, |
| 120 | + )} |
| 121 | + style={width ? `white-space: pre-wrap; width: ${width}px;` : null} |
| 122 | + > |
| 123 | + <div class="flex gap-2"> |
| 124 | + <slot name="content"> |
| 125 | + {#if icon}<Icon name={icon} class="inline h-4" />{/if} |
| 126 | + <span>{text}</span> |
| 127 | + </slot> |
| 128 | + </div> |
| 129 | + </div> |
| 130 | + </Portal> |
| 131 | + {:else} |
96 | 132 | <div |
97 | 133 | class={merge( |
98 | | - 'inline-block rounded-md bg-slate-800 px-2 py-2 text-slate-50', |
99 | | - tooltipClass, |
| 134 | + 'tooltip absolute left-0 top-0 z-50 hidden translate-x-12 whitespace-nowrap text-xs opacity-0 transition-all group-hover:inline-block group-hover:opacity-95', |
| 135 | + show && 'inline-block opacity-95', |
100 | 136 | )} |
| 137 | + class:left |
| 138 | + class:right |
| 139 | + class:bottom |
| 140 | + class:bottomLeft |
| 141 | + class:bottomRight |
| 142 | + class:top |
| 143 | + class:topRight |
| 144 | + class:topLeft |
| 145 | + style={width ? `white-space: pre-wrap; width: ${width}px;` : null} |
101 | 146 | > |
102 | | - <div class="flex gap-2"> |
103 | | - <slot name="content"> |
104 | | - {#if icon}<Icon name={icon} class="inline h-4" />{/if} |
105 | | - <span>{text}</span> |
106 | | - </slot> |
| 147 | + <div |
| 148 | + class={merge( |
| 149 | + 'inline-block rounded-md bg-slate-800 px-2 py-2 text-slate-50', |
| 150 | + tooltipClass, |
| 151 | + )} |
| 152 | + > |
| 153 | + <div class="flex gap-2"> |
| 154 | + <slot name="content"> |
| 155 | + {#if icon}<Icon name={icon} class="inline h-4" />{/if} |
| 156 | + <span>{text}</span> |
| 157 | + </slot> |
| 158 | + </div> |
107 | 159 | </div> |
108 | 160 | </div> |
109 | | - </div> |
| 161 | + {/if} |
110 | 162 | </div> |
111 | 163 | {/if} |
112 | 164 |
|
|
0 commit comments