|
1 | 1 | # sb-floating-panel-vue |
| 2 | + |
2 | 3 | [](https://www.npmjs.com/package/sb-floating-panel-vue) |
3 | 4 | [](https://github.com/stefanBid/sb-floating-panel-vue/blob/main/LICENSE) |
| 5 | +[](https://vuejs.org) |
| 6 | +[](#) |
| 7 | + |
| 8 | +## 🔥 Features |
| 9 | + |
| 10 | +- 🧩 Floating panel positioning with arrow and smart shifting |
| 11 | +- ⚡ Built on top of [`@floating-ui/vue`](https://floating-ui.com/) |
| 12 | +- 🎯 Support for resize syncing and animated transitions |
| 13 | +- 🧪 Dual usage: composable or plug-and-play components |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## 👤 Author |
4 | 18 |
|
5 | | -> A lightweight Vue 3 composable hook built on top of [@floating-ui/vue](https://github.com/floating-ui/floating-ui) to manage floating panels with arrow and resize support. |
| 19 | +Created with ❤️ by [Stefano Biddau](https://www.stefano-biddau.com/) |
6 | 20 |
|
7 | 21 | --- |
8 | 22 |
|
9 | | -## ✨ Features |
| 23 | +## ❓ Why This Library? |
10 | 24 |
|
11 | | -- 📦 Minimal and composable API |
12 | | -- 🎯 Precise floating panel positioning via `@floating-ui/vue` |
13 | | -- 🎈 Optional arrow positioning |
14 | | -- 📏 Auto-resize support |
15 | | -- 🧠 TypeScript-first |
| 25 | +`sb-floating-panel-vue` was designed to make it extremely easy to implement dropdowns, tooltips, and floating panels in Vue 3, without worrying about placement logic, arrow positioning, or responsive syncing. It wraps `@floating-ui/vue` into a more ergonomic composable or ready-to-use components. |
16 | 26 |
|
17 | 27 | --- |
18 | 28 |
|
19 | | -## 🚀 Installation |
| 29 | +## 📦 Installation |
20 | 30 |
|
21 | 31 | ```bash |
| 32 | +# npm |
22 | 33 | npm install sb-floating-panel-vue |
23 | | -# or |
| 34 | + |
| 35 | +# yarn |
24 | 36 | yarn add sb-floating-panel-vue |
| 37 | + |
| 38 | +# pnpm |
| 39 | +pnpm add sb-floating-panel-vue |
25 | 40 | ``` |
26 | 41 |
|
27 | 42 | --- |
28 | 43 |
|
29 | | -## 🧩 Usage |
| 44 | +## 🧠 Composable Usage |
30 | 45 |
|
31 | | -The `useSbFloatingPanel` composable provides an easy-to-integrate API to handle floating panels, such as tooltips, popovers, dropdowns, and other UI overlays. |
| 46 | +Use the composable `useSbFloatingPanel()` for complete control. |
32 | 47 |
|
33 | | -### Importing |
| 48 | +### Options |
| 49 | + |
| 50 | + |
| 51 | +| Option | Type | Required | Default | Description | |
| 52 | +|------------|------------|----------|---------|--------------------------------------------------| |
| 53 | +| placement | `Placement` | Yes | — | Placement of the floating panel | |
| 54 | +| strategy | `'absolute'` \| `'fixed'` | Yes | — | CSS positioning strategy | |
| 55 | +| offsetValue| `number` | Yes | — | Offset between reference and floating | |
| 56 | +| hasArrow | `boolean` | No | `false` | Whether to render arrow | |
| 57 | +| hasResize | `boolean` | No | `false` | Sync width of floating to reference | |
| 58 | + |
| 59 | +### Return Values |
| 60 | + |
| 61 | +| Property | Type | Description | |
| 62 | +|-----------------------|--------------------------------------|----------------------------------------| |
| 63 | +| reference | `Ref<HTMLElement \| null>` | Ref for the trigger element | |
| 64 | +| floating | `Ref<FloatingElement \| null>` | Ref for the floating panel | |
| 65 | +| floatingArrow | `Ref<HTMLElement \| null>` | Ref for the arrow element | |
| 66 | +| floatingPlacement | `Ref<Placement>` | The actual resolved placement | |
| 67 | +| floatingStyle | `ComputedRef<CSSProperties>` | Style for the floating panel | |
| 68 | +| floatingArrowStyle | `ComputedRef<CSSProperties>` | Style for the arrow | |
| 69 | +| isOpen | `Ref<boolean>` | Panel visibility | |
| 70 | +| toggle / open / close | `() => void` | Control methods to toggle visibility | |
34 | 71 |
|
35 | | -```ts |
36 | | -import { useSbFloatingPanel } from 'sb-floating-panel-vue'; |
37 | | -``` |
38 | 72 |
|
39 | | -### 🧠 API |
| 73 | +### Example |
| 74 | +In your Vue component, you can use the composable like this: |
40 | 75 |
|
| 76 | +In your script tag, import the composable and use it: |
41 | 77 | ```ts |
| 78 | +import { useSbFloatingPanel } from 'sb-floating-panel-vue'; |
| 79 | + |
42 | 80 | const { |
43 | 81 | reference, |
44 | 82 | floating, |
45 | | - popperArrow, |
| 83 | + floatingArrow, |
| 84 | + floatingStyle, |
| 85 | + floatingArrowStyle, |
46 | 86 | isOpen, |
47 | | - floatingPosition, |
48 | | - floatingStyles, |
49 | | - floatingArrowStyles, |
50 | | - changeFloatingVisibility, |
51 | | -} = useSbFloatingPanel(settings); |
| 87 | + toggle, |
| 88 | +} = useSbFloatingPanel({ |
| 89 | + placement: 'bottom-start', |
| 90 | + strategy: 'absolute', |
| 91 | + offsetValue: 12, |
| 92 | + hasArrow: true, |
| 93 | +}); |
52 | 94 | ``` |
| 95 | +In your template, you can bind the refs and styles: |
| 96 | +```html |
| 97 | +<template> |
| 98 | + <button ref="reference" @click="toggle()">Open Panel</button> |
| 99 | + <div v-if="isOpen" ref="floating" :style="floatingStyle"> |
| 100 | + Panel |
| 101 | + <div ref="floatingArrow" :style="floatingArrowStyle" /> |
| 102 | + </div> |
| 103 | +</template> |
| 104 | +``` |
| 105 | + |
| 106 | +--- |
53 | 107 |
|
54 | | -### Parameters |
| 108 | +## 🧱 Component-Based Usage |
55 | 109 |
|
56 | | -The composable takes a `settings` object: |
| 110 | +### Import the style |
| 111 | + |
| 112 | +If you use components, you **must** import styles in `main.ts`: |
57 | 113 |
|
58 | 114 | ```ts |
59 | | -interface FloatingSettings { |
60 | | - placement: Placement; // e.g., 'bottom', 'top-start', etc. |
61 | | - strategy: Strategy; // 'absolute' | 'fixed' |
62 | | - offsetValue: number; // offset in pixels from the reference element |
63 | | - hasArrow?: boolean; // enable arrow positioning |
64 | | - hasResize?: boolean; // enable auto-resizing with anchor element |
65 | | -} |
| 115 | +import 'sb-floating-panel-vue/style.css'; |
66 | 116 | ``` |
67 | 117 |
|
68 | | ---- |
| 118 | +### Available Components |
69 | 119 |
|
70 | | -## 🔍 Computed Values Explained |
| 120 | +#### `<SbContainer>` |
71 | 121 |
|
72 | | -### `floatingStyles` |
| 122 | +Wraps everything and provides the context. |
73 | 123 |
|
74 | | -A Vue `computed` value containing the CSS style object that must be applied to your floating panel. It is dynamically calculated by `@floating-ui/vue` to maintain optimal placement relative to the reference element. |
| 124 | +| Prop | Type | Default | Description | |
| 125 | +|-------------|---------------------------|-----------------|-----------------------------------------------------------------------------| |
| 126 | +| placement | `Placement` | `'bottom-start'`| Placement of the floating panel relative to the reference element | |
| 127 | +| strategy | `'absolute' \| 'fixed'` | `'absolute'` | CSS positioning strategy for the floating panel | |
| 128 | +| offsetValue | `number` | `15` | Offset in pixels between reference and floating panel | |
| 129 | +| hasArrow | `boolean` | `false` | Whether to render an arrow pointing to the reference | |
| 130 | +| hasResize | `boolean` | `false` | Whether the floating panel should sync its width with the reference | |
75 | 131 |
|
76 | | -### `floatingPosition` |
77 | 132 |
|
78 | | -The actual resolved placement string, e.g., `'bottom-start'`, useful for adding custom animations or behavior depending on where the panel is placed. |
79 | 133 |
|
80 | | -### `floatingArrowStyles` |
| 134 | +#### `<SbReference>` |
81 | 135 |
|
82 | | -An object that includes styles for positioning the optional arrow element (enabled via the `hasArrow` setting). You can directly bind this to the arrow’s `style` attribute. |
| 136 | +Trigger element |
83 | 137 |
|
84 | | ---- |
| 138 | +| Prop | Type | Default | Description | |
| 139 | +|--------------|-----------------------------------|--------------|------------------------------------------------------------------------------| |
| 140 | +| referenceRef | `Ref<HTMLElement \| null>` | **required** | Reference element to which the floating panel is attached | |
| 141 | +| as | `'button' \| 'div' \| 'span' \| etc.` | `'button'` | HTML tag to render. Defaults to `<button>` for accessibility | |
| 142 | +| onClick | `() => void` | — | Optional click handler, often used to toggle the floating panel visibility | |
85 | 143 |
|
86 | | -## 🧪 Example |
87 | 144 |
|
88 | | -```ts |
89 | | -<script setup lang="ts"> |
90 | | -import { ref } from 'vue'; |
91 | | -import { useSbFloatingPanel } from 'sb-floating-panel-vue'; |
| 145 | +#### `<SbFloating>` |
92 | 146 |
|
93 | | -const { |
94 | | - reference, |
95 | | - floating, |
96 | | - popperArrow, |
97 | | - isOpen, |
98 | | - floatingStyles, |
99 | | - floatingArrowStyles, |
100 | | - changeFloatingVisibility, |
101 | | -} = useSbFloatingPanel({ |
102 | | - placement: 'bottom', |
103 | | - strategy: 'absolute', |
104 | | - offsetValue: 8, |
105 | | - hasArrow: true, |
106 | | - hasResize: true, |
107 | | -}); |
| 147 | +Floating panel |
| 148 | + |
| 149 | +| Prop | Type | Default | Description | |
| 150 | +|---------------------|----------------------------------------------|-----------------|-----------------------------------------------------------------------------| |
| 151 | +| floatingRef | `Ref<HTMLElement \| null>` | **required** | The target element for the floating panel (popover, tooltip, etc.) | |
| 152 | +| floatingArrowRef | `Ref<HTMLElement \| null>` | optional | The element used as the floating arrow | |
| 153 | +| isOpen | `boolean` | **required** | Controls visibility of the floating panel | |
| 154 | +| floatingPlacement | `Placement` | **required** | Current placement as resolved by `@floating-ui/vue` | |
| 155 | +| floatingStyle | `CSSProperties` | **required** | Inline style object applied to the floating panel | |
| 156 | +| floatingArrowStyle | `CSSProperties` | optional | Inline style object applied to the arrow element | |
| 157 | +| arrowDimensions | `number` | optional | Size (width/height) of the arrow in pixels | |
| 158 | +| zIndex | `number` | `undefined` | Custom z-index applied to the floating and arrow elements | |
| 159 | +| animation | `'fade' \| 'scale-fade' \| 'none'` | `'scale-fade'` | Defines the animation style for enter/leave transitions | |
| 160 | +| duration | `number` | `300` | Duration in ms for the transition animations | |
| 161 | + |
| 162 | + |
| 163 | +--- |
| 164 | + |
| 165 | +### 🧪 Component Usage Example |
108 | 166 |
|
109 | | -const togglePanel = () => { |
110 | | - changeFloatingVisibility(!isOpen.value); |
111 | | -}; |
112 | | -</script> |
| 167 | +In your project, you can use the components like this: |
| 168 | + |
| 169 | +In your script tag, import the components: |
| 170 | +```ts |
| 171 | +import { SbContainer, SbFloating, SbReference } from 'sb-floating-panel-vue'; |
113 | 172 | ``` |
114 | 173 |
|
115 | | -```vue |
116 | | -<template> |
117 | | - <button ref="reference" @click="togglePanel">Toggle Panel</button> |
118 | | - <div |
119 | | - v-if="isOpen" |
120 | | - ref="floating" |
121 | | - :style="floatingStyles" |
122 | | - class="popover" |
123 | | - > |
124 | | - I'm a floating panel! |
125 | | - <div ref="popperArrow" :style="floatingArrowStyles.arrow" class="arrow" /> |
126 | | - </div> |
127 | | -</template> |
| 174 | +In your template, you can use the components as follows: |
| 175 | +```html |
| 176 | +<SbContainer :has-arrow="true" strategy="fixed"> |
| 177 | + <template #default="{ |
| 178 | + reference, |
| 179 | + floating, |
| 180 | + floatingArrow, |
| 181 | + floatingPlacement, |
| 182 | + floatingStyle, |
| 183 | + floatingArrowStyle, |
| 184 | + isOpen, |
| 185 | + toggle |
| 186 | + }"> |
| 187 | + <SbReference :reference-ref="reference" :onClick="toggle">Toggle</SbReference> |
| 188 | + <SbFloating |
| 189 | + :is-open="isOpen.value" |
| 190 | + :floating-ref="floating" |
| 191 | + :floating-arrow-ref="floatingArrow" |
| 192 | + :floating-placement="floatingPlacement.value" |
| 193 | + :floating-style="floatingStyle.value" |
| 194 | + :floating-arrow-style="floatingArrowStyle.value" |
| 195 | + :arrow-dimensions="12" |
| 196 | + :z-index="200" |
| 197 | + > |
| 198 | + Hello Panel! |
| 199 | + </SbFloating> |
| 200 | + </template> |
| 201 | +</SbContainer> |
128 | 202 | ``` |
129 | 203 |
|
130 | 204 | --- |
131 | 205 |
|
132 | | -## 🎨 Styling |
133 | | - |
134 | | -You can use your own CSS or utility classes (like Tailwind CSS) to style the panel and the arrow. Example: |
135 | | - |
136 | | -```css |
137 | | -.popover { |
138 | | - background: white; |
139 | | - border: 1px solid #ccc; |
140 | | - padding: 0.75rem; |
141 | | - border-radius: 0.5rem; |
142 | | - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
143 | | -} |
144 | | - |
145 | | -.arrow { |
146 | | - width: 10px; |
147 | | - height: 10px; |
148 | | - background: white; |
149 | | - transform: rotate(45deg); |
150 | | - position: absolute; |
151 | | -} |
| 206 | +## ⚠️ Directive Note (Teleport Limitation) |
| 207 | + |
| 208 | +Due to `<teleport>` in `<SbFloating>`, if you apply custom directives directly to the component, Vue will warn: |
| 209 | + |
| 210 | +``` |
| 211 | +[Vue warn]: Extraneous non-props attributes ([directive-name]) were passed to component but could not be automatically inherited because component renders fragment or text or teleport root nodes. |
| 212 | +``` |
| 213 | + |
| 214 | +**Solution**: Move the directive to an inner element using the slot. |
| 215 | + |
| 216 | +### Example Fix |
| 217 | + |
| 218 | +```vue |
| 219 | +<SbFloating ...> |
| 220 | + <div v-click-outside>...</div> |
| 221 | +</SbFloating> |
152 | 222 | ``` |
153 | 223 |
|
154 | 224 | --- |
| 225 | + |
155 | 226 | ## 📄 License |
156 | 227 |
|
157 | | -This project is licensed under the [MIT License](./LICENSE) © 2025 [Stefano Biddau](https://github.com/stefanBid) |
| 228 | +[MIT License](https://github.com/stefanBid/sb-floating-panel-vue/blob/main/LICENSE) © [Stefano Biddau](https://www.stefano-biddau.com/) |
0 commit comments